Java - instanceof

2020. 6. 14. 16:16Java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
public class Ex {
 
    public static void main(String[] args) {
        // 원, 사각형, 삼각형 도형을 그리기 위해 
        // 각 도형 클래스의 인스턴스 생성 후 레퍼런스변수명.draw() 메서드를 호출
        Circle c = new Circle();
        c.draw();
        
        Rectangle r = new Rectangle();
        r.draw();
        
        Triangle t = new Triangle();
        t.draw();
        
        System.out.println("====================================");
        
        // 업캐스팅을 활용하여 Shape 타입 레퍼런스를 통해 각 도형클래스의 인스턴스 참조
        Shape s; // 슈퍼클래스(Shape) 타입 레퍼런스 변수 선언
        
//        s = c; // 업캐스팅
//        s.draw();
//        
//        s = r; // 업캐스팅
//        s.draw();
//        
//        s = t; // 업캐스팅
//        s.draw();
 
        
        // Circle -> Shape 업캐스팅
        s = new Circle(); // 업캐스팅이므로 자동 형변환
        // 업캐스팅 후에는 상속된 메서드만 호출 가능하며
        // Circle 클래스 내에서 draw() 메서드를 상속받아 오버라이딩 했으므로
        s.draw(); // Shape 타입 레퍼런스 변수로 draw() 메서드 호출이 가능하다!
        // => 실제 호출된 메서드는 Circle 인스턴스 내의 "원 그리기!" 가 출력되는 draw() 메서드 
    
        // Rectangle -> Shape 업캐스팅
        s = new Rectangle();
        s.draw();
        
        // Triangle -> Shape 업캐스팅
        s = new Triangle();
        s.draw();
        
        
        System.out.println("====================================");
        
        // 배열을 사용하여 업캐스팅을 활용하는 방법
        // 3개의 인스턴스를 하나의 Shape 타입 배열에 저장
//        Shape[] sArr = {c, r, t}; // 레퍼런스 변수 전달 또는
        Shape[] sArr = {new Circle(), new Rectangle(), new Triangle()}; // 인스턴스 생성 후 전달
        
        // for문을 사용하여 Shape[] 타입 배열 sArr 의 모든 요소에 접근하여 draw() 메서드 호출
//        for(int i = 0; i < sArr.length; i++) {
//            // 배열 내의 요소마다 draw() 메서드 호출 가능
//            sArr[i].draw();
//        }
        
        // 향상된 for문 사용
        for(Shape shape : sArr) {
            shape.draw();
        }
            
        
        System.out.println("=============================================");
        // 메서드의 파라미터로 업캐스팅을 활용하는 방법
        // 일반 업캐스팅
//        Shape s2;
//        s2 = c;
//        s2.draw();
//        
//        s2 = r;
//        s2.draw();
//        
//        s2 = t;
//        s2.draw();
        
        // 메서드 파라미터에 대한 업캐스팅
        polymorphismDraw(c); // Circle 인스턴스 전달(Circle -> Shape 타입 업캐스팅)
        polymorphismDraw(r); // Rectangle 인스턴스 전달(Rectangle -> Shape 타입 업캐스팅)
        polymorphismDraw(t); // Triangle 인스턴스 전달(Triangle -> Shape 타입 업캐스팅)
        // => Circle, Rectangle, Triangle 인스턴스를 모두 전달받기 위해서는
        //    세 인스턴스의 슈퍼클래스 타입인 Shape 타입 변수를 파라미터 변수로 선언 필요!
        
         
        System.out.println("================================");
        
        // 모든 인스턴스는 Object 타입으로 업캐스팅이 가능하다!
        // => Object 클래스는 모든 클래스의 최상위 클래스이므로
        Object o = c;
//        o.draw(); // Object 클래스에 정의되지 않은 메서드이므로 호출 불가!
        
        o = r;
        
        o = t;
        
        System.out.println(c);
        System.out.println(r);
        System.out.println(t);
        
        
    }
    
    // Circle, Rectangle, Triangle 클래스의 인스턴스를 전달받아 
    // 각 인스턴스 내의 draw() 메서드를 호출하는 polymorphismDraw() 메서드 정의
    public static void polymorphismDraw(Shape s) {
        // 어떤 도형의 인스턴스가 전달되더라도 Shape 클래스의 서브클래스 타입이면
        // 업캐스팅을 통해 공통된 Shape 타입 변수 하나만으로 draw() 메서드 호출이 가능하다!
        s.draw();
    }
    
 
}
 
class Shape { // 도형 클래스
    
    public void draw() {
        System.out.println("도형 그리기!");
    }
    
}
 
class Circle extends Shape { // 원 클래스 => 도형(Shape) 클래스를 상속받아 정의
 
    // Shape 클래스의 draw() 메서드 오버라이딩
    // => "원 그리기!" 로 메서드 작업 변경
    @Override
    public void draw() {
        System.out.println("원 그리기!");
    } 
    
    public void circleInfo() {
        System.out.println("원의 정보 표시!");
    }
    
}
 
// 사각형(Rectangle) 클래스 => 도형(Shape) 클래스를 상속받아 정의
// Shape 클래스의 draw() 메서드 오버라이딩
// => "사각형 그리기!" 로 메서드 작업 변경
class Rectangle extends Shape { // 원 클래스 => 도형(Shape) 클래스를 상속받아 정의
 
    @Override
    public void draw() {
        System.out.println("사각형 그리기!");
    } 
    
}
 
 
// 삼각형(Triangle) 클래스 => 도형(Shape) 클래스를 상속받아 정의
// Shape 클래스의 draw() 메서드 오버라이딩
// => "삼각형 그리기!" 로 메서드 작업 변경
class Triangle extends Shape { // 원 클래스 => 도형(Shape) 클래스를 상속받아 정의
 
    @Override
    public void draw() {
        System.out.println("삼각형 그리기!");
    } 
    
}
 
public class Ex2 {
 
    public static void main(String[] args) {
        /*
         * instanceof 연산자
         * - 어떤 객체에 대한 is-a 관계를 판별하는 연산자
         * - 주로 if문을 사용하여 형변환이 가능한 지 여부 판별하는 용도로 사용
         * - 연산자 좌변에는 판별에 필요한 객체의 레퍼런스 변수를 지정하고
         *   연산자 우변에는 is-a 관계 판별에 사용될 클래스명을 지정
         *   => 좌변의 객체가 우변의 클래스 타입으로 형변환이 가능한지 여부 판별
         * - A is a B 에 대한 판별 문법은 A instanceof B 형태로 사용
         *   (A : 객체의 레퍼런스 변수명, B : 클래스명)
         * - instanceof 연산 결과가 true 이면 형변환 가능(업캐스팅 또는 다운캐스팅)
         *              ""          false 이면 어떤 방법으로든 형변환 불가능
         * 
         * < 기본 문법 >
         * if(판별할 객체의 레퍼런스 변수 instanceof 클래스명) {}
         */
        
        SmartPhone sp = new SmartPhone("갤럭시""010-1234-5678""안드로이드");
        
        // instanceof 연산자는 좌변의 객체가 우변의 클래스타입인지 판별하는 연산자
        if(sp instanceof SmartPhone) { // sp 는 SmartPhone 입니까? = sp is a SmartPhone?
            System.out.println("sp 는 SmartPhone 입니다!");
            // 그러므로 레퍼런스 변수 sp 를 SmartPhone 타입으로 형변환이 가능
            SmartPhone s = sp; // 업캐스팅
        } else {
            System.out.println("sp 는 SmartPhone 이 아닙니다!");
        }
        
        System.out.println("------------------");
        
        // sp 는 HandPhone 입니까? = sp is a HandPhone?
        if(sp instanceof HandPhone) {
            System.out.println("sp 는 HandPhone 입니다!");
            System.out.println("그러므로 sp 를 HandPhone 으로 형변환 가능!");
            HandPhone hp = sp; // sp -> HandPhone 업캐스팅
            System.out.println("sp 는 HandPhone 이 가진 기능을 모두 포함!");
            
            System.out.println("업캐스팅 후에는 공통 멤버만 접근 가능!");
            hp.call();
            hp.sms();
//            hp.kakaoTalk(); // SmartPhone 의 멤버에는 접근 불가
//            hp.youtube(); // SmartPhone 의 멤버에는 접근 불가
        } else {
            System.out.println("sp 는 HandPhone 이 아닙니다!");
        }
        
        System.out.println("-------------------");
        
        // HandPhone 타입 인스턴스 생성
        HandPhone hp = new HandPhone("애니콜""010-1111-2222");
        
        // hp 는 SmartPhone 입니까?
        if(hp instanceof SmartPhone) {
            System.out.println("hp 는 SmartPhone 입니다!");
        } else {
            System.out.println("hp 는 SmartPhone 이 아닙니다!");
            System.out.println("그러므로 hp 는 SmartPhone 으로 형변환 불가능!");
//            SmartPhone sp2 = hp; // 컴파일 에러 발생 => 강제형변환을 통해 문법 오류 해결
//            SmartPhone sp2 = (SmartPhone)hp; // 문법 오류가 해결되더라도 실행 시점에서 오류 발생!
            
            System.out.println("hp 는 SmartPhone 이 가진 기능을 모두 포함하지 못함!");
        }
        
        System.out.println("=============================");
        
        // HandPhone -> SmartPhone 으로 다운캐스팅 해도 안전한 경우
        // => 업캐스팅 후 다시 다운캐스팅 하는 경우에만 안전하다!
        HandPhone hp2 = new SmartPhone("갤럭시""010-1234-5678""안드로이드"); // 업캐스팅
        
        // hp2 는 SmartPhone 입니까?
        if(hp2 instanceof SmartPhone) {
            System.out.println("hp2 는 SmartPhone 입니다!");
            System.out.println("그러므로 hp2 는 SmartPhone 으로 형변환 가능!");
//            SmartPhone sp2 = hp2; // 컴파일에러가 발생하므로 다운캐스팅 필요
            SmartPhone sp2 = (SmartPhone)hp2; // 다운캐스팅
            System.out.println("다운캐스팅 후에는 원래 SmartPhone 의 기능을 모두 사용 가능");
            sp2.call();
            sp2.sms();
            sp2.kakaoTalk();
            sp2.youtube();
            System.out.println("SmartPhone 은 hp2 의 기능을 모두 포함!");
        } else {
            System.out.println("hp2 는 SmartPhone 이 아닙니다!");
        }
        
        
    }
 
}
 
class HandPhone {
    String modelName;
    String number;
    
    public HandPhone(String modelName, String number) {
        super();
        this.modelName = modelName;
        this.number = number;
    }
 
    public void call() {
        System.out.println("HandPhone - 전화 기능!");
    }
    
    public void sms() {
        System.out.println("HandPhone - 문자 기능!");
    }
}
 
class SmartPhone extends HandPhone {
    String osName;
 
    public SmartPhone(String modelName, String number, String osName) {
        super(modelName, number);
        this.osName = osName;
    }
    
    public void kakaoTalk() {
        System.out.println("SmartPhone - 카톡 기능!");
    }
    
    public void youtube() {
        System.out.println("SmartPhone - 유튜브 기능!");
    }
    
}
public class Test2 {
 
    public static void main(String[] args) {
 
        System.out.println("------- Taxi -> Car 업캐스팅 -------");
        // Taxi -> Car 타입으로 업캐스팅
        // 호출 가능한 메서드 : 2개
//        Car car = new Taxi(240);
//        // 업캐스팅 후에는 공통 메서드만 호출 가능
//        car.speedUp(50);
//        car.speedDown(100);
        
        // 형변환 가능 여부 확인 후 형변환 수행하기
        Taxi taxi = new Taxi(240);
        
        // Taxi -> Car 로 형변환 가능 여부 판별
        Car car = null// 로컬변수 Car 타입 선언 => null 값으로 초기화
        
        if(taxi instanceof Car) {
            car = taxi;
            // 업캐스팅 후에는 공통 메서드만 호출 가능
            car.speedUp(50);
            car.speedDown(100);
        }
        
        
        System.out.println("------- Car -> Taxi 다운캐스팅 --------");
        // 업캐스팅 후 다시 다운캐스팅 시 호출 가능한 메서드 : ?개
//        Taxi taxi2 = car; // 오류 발생! 명시적 형변환 필수
//        Taxi taxi2 = (Taxi)car;
//        taxi2.speedUp(100);
//        taxi2.speedDown(100);
//        taxi2.lift();
//        taxi2.drop();
        
        // car -> Taxi 로 형변환 가능 여부 판별
        if(car instanceof Taxi) {
            Taxi taxi2 = (Taxi)car;
            taxi2.speedUp(100);
            taxi2.speedDown(100);
            taxi2.lift();
            taxi2.drop();
        }
        
        System.out.println("------- Truck -> Car 업캐스팅 -------");
        // Truck -> Car 타입으로 업캐스팅
        // 호출 가능한 메서드 : 2개
//        car = new Truck(240);
//        car.speedUp(50);
//        car.speedDown(100);
        
        
        Truck truck = new Truck(240);
        // truck -> Car 타입으로 업캐스팅 가능 여부 판별
        if(truck instanceof Car) {
            car = truck;
            car.speedUp(50);
            car.speedDown(100);
        }
        
        
        System.out.println("------- Car -> Truck 다운캐스팅 -------");
        
        if(car instanceof Truck) {
            truck = (Truck)car;
            truck.speedUp(100);
            truck.speedDown(100);
            truck.dump();
        }
        
        
    }
 
}
 
// 슈퍼클래스 타입 Car 클래스 정의
// 멤버변수 : 속력(speed, 정수), 최대속력(maxSpeed, 정수)
// 생성자 : 최대속력을 전달받아 초기화
// 메서드 : 1) speedUp() - 파라미터 있음(speed, 정수) => "Car 속력 증가!" 출력
//                       - 리턴값 없음
//          2) speedDown() - 파라미터 있음(speed, 정수) => "Car 속력 감소!" 출력
//                           - 리턴값 없음
class Car {
    int speed;
    int maxSpeed;
    
//    public Car() {}
    
    public Car(int maxSpeed) {
        super();
        this.maxSpeed = maxSpeed;
    }
    
    public void speedUp(int speed) {
        System.out.println("Car 속력 증가!");
    }
    
    public void speedDown(int speed) {
        System.out.println("Car 속력 감소!");
    }
    
}
 
// Taxi 클래스 정의 - Car 클래스 상속
// 메서드 오버라이딩
// 1) speedUp() : "Taxi 속력 증가!" 출력
// 2) speedDown() : "Taxi 속력 감소!" 출력
// 메서드 정의
// lift() : "손님 승차!" 출력 => 파라미터, 리턴값 없음
// drop() : "손님 하차!" 출력 => 파라미터, 리턴값 없음
class Taxi extends Car {
    
    public Taxi(int maxSpeed) {
        super(maxSpeed);
    }
 
    @Override
    public void speedUp(int speed) {
        System.out.println("Taxi 속력 증가!");
    }
 
    @Override
    public void speedDown(int speed) {
        System.out.println("Taxi 속력 감소!");
    }
    
    public void lift() {
        System.out.println("손님 승차!");
    }
    
    public void drop() {
        System.out.println("손님 하차!");
    }
    
}
 
// Truck 클래스 정의 - Car 클래스 상속
// 메서드 오버라이딩
// 1) speedUp() : "Truck 속력 증가!" 출력
// 2) speedDown() : "Truck 속력 감소!" 출력
// 메서드 정의
// dump() : "짐칸의 모든 내용물 쏟아붓기!" 출력
class Truck extends Car {
 
    public Truck(int maxSpeed) {
        super(maxSpeed);
    }
 
    @Override
    public void speedUp(int speed) {
        System.out.println("Truck 속력 증가!");
    }
 
    @Override
    public void speedDown(int speed) {
        System.out.println("Truck 속력 감소!");
    }
    
    public void dump() {
        System.out.println("짐칸의 모든 내용물 쏟아붓기!");
    }
    
}
 
cs

도형을 클래스로 정의해서 저번 시간에 배웠던 업 캐스팅 다운 캐스팅에 대해 복습해 보았고 개념을 좀 더 확실히 잡을 수 있었다. 그리고 내가 걱정했었던 여러 클래스와 메서드가 복잡하게 얽히면서 상속관계가 분명치 않을 때가 있을 거라고 생각했었던 경우를 대비해서 instanceof라는 문법에 대해서 배워보았는데 아주 좋은 걸 배운 거 같다. 여기서도 if문 조건식을 통해 instanceof를 사용하여 상속관계가 맞다면 실행할 문장을 정의해 놓고 하니 복잡하게 찾아다닐 필요도 줄어들고 좋은 거 같았다. 자동차의 여러 종류로 클래스를 정의해 배워 보았던 instanceof 도 사용해 보고 업 캐스팅, 다운 캐스팅을 좀 더 연습해보며 개념을 다잡을 수 있었다. 역시 복습이 필요한 거 같다.