본문 바로가기
자바(Java)/자바기초

[자바기초.018] 다형성(Polymorphism)

by 긱펀 2024. 3. 17.
반응형

[자바기초.018] 다형성(Polymorphism)

 

[1] 다형성(Polymorphism)이란?

  • 다형성(Polymorphism)은 poly + morphism의 합성어이다.
  • poly: 많은(many)
  • morphism: form(형태)
  • 즉, 다형성(Polymorphism)은 "많은 형태(many form)"라는 뜻으로 직역된다.
  • 자바(Java)에서 다형성(polymorphism)이란, 하나의 객체(object)가 여러 가지 타입(type)을 가질 수 있는 것을 의미합니다.
  • 다형성은 상속, 추상화와 더불어 객체 지향 프로그래밍을 구성하는 중요한 특징 중 하나입니다.
  • 자바에서는 이러한 다형성을 부모 클래스 타입의 참조 변수로 자식 클래스 타입의 인스턴스를 참조할 수 있도록 하여 구현하고 있습니다.(ex: Parent p = new Child(); )

 

[2] 다형성의 예시

1
2
3
4
5
6
7
8
9
10
11
12
13
class Parent { ... }
 
class Child extends Parent { ... }
 
...
 
Parent pa = new Parent(); // 허용
 
Child ch = new Child();   // 허용
 
Parent pc = new Child();  // 허용
 
Child cp = new Parent();  // 오류 발생.
cs
  • 특정 타입의 참조 변수로는 당연히 같은 타입의 인스턴스를 참조할 수 있습니다.
  • 참조 변수가 사용할 수 있는 멤버의 개수가 실제 인스턴스의 멤버 개수와 같기 때문입니다.
  • 그리고 부모 클래스 타입의 참조 변수로도 자식 클래스 타입의 인스턴스를 참조할 수 있습니다. 이유는, 참조 변수가 사용할 수 있는 멤버의 개수가 실제 인스턴스의 멤버 개수보다 적기 때문입니다.
  • 하지만 반대의 경우인 자식 클래스 타입의 참조 변수로는 부모 클래스 타입의 인스턴스를 참조할 수 없습니다.
  • 참조 변수가 사용할 수 있는 멤버의 개수가 실제 인스턴스의 멤버 개수보다 많기 때문입니다.
[중요]
클래스는 상속을 통해 확장될 수는 있어도 축소될 수는 없으므로, 자식 클래스에서 사용할 수 있는 멤버의 개수가 언제나 부모 클래스와 같거나 많게 됩니다.

 

 

[3] 참조 변수의 타입 변환

자바에서는 참조 변수도 다음과 같은 조건에 따라 타입 변환을 할 수 있습니다.

1. 서로 상속 관계에 있는 클래스 사이에만 타입 변환을 할 수 있습니다.
2. 자식 클래스 타입에서 부모 클래스 타입으로의 타입 변환은 생략할 수 있습니다.
3. 하지만 부모 클래스 타입에서 자식 클래스 타입으로의 타입 변환은 반드시 명시해야 합니다.

참조 변수의 타입 변환도 기본 타입의 타입 변환과 마찬가지로 타입 캐스트 연산자(())를 사용합니다.

<타입 캐스트 연산자 문법>
(변환할타입의클래스이름) 변환할참조변수

 

다음 예제는 참조 변수의 타입 변환을 보여주는 예제입니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Parent { ... }
 
class Child extends Parent { ... }
 
class Brother extends Parent { ... }
 
...
 
Parent pa01 = null;
Child ch = new Child();
Parent pa02 = new Parent();
Brother br = null;
 
pa01 = ch;          // pa01 = (Parent)ch; 와 같으며, 타입 변환을 생략할 수 있음.
br = (Brother)pa02; // 타입 변환을 생략할 수 없음.
br = (Brother)ch;   // 직접적인 상속 관계가 아니므로, 오류 발생.
cs

 

 

[4] instance of 연산자

  • 이러한 다형성으로 인해 런타임에 참조 변수가 실제로 참조하고 있는 인스턴스의 타입을 확인할 필요성이 생깁니다.
  • 자바에서는 instanceof 연산자를 제공하여, 참조 변수가 참조하고 있는 인스턴스의 실제 타입을 확인할 수 있도록 해줍니다.

자바에서 instanceof 연산자는 다음과 같이 사용합니다.

<instanceof 문법>
참조변수 instanceof 클래스이름

 

  • 왼쪽에 전달된 참조 변수가 실제로 참조하고 있는 인스턴스의 타입이 오른쪽에 전달된 클래스 타입이면 true를 반환하고, 아니면 false를 반환합니다.
  • 만약에 참조 변수가 null을 가리키고 있으면 false를 반환합니다.

다음 예제는 참조 변수가 실제로 가리키고 있는 인스턴스의 타입을 instanceof 연산자로 확인하는 예제입니다.

 

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
class Parent { }
 
class Child extends Parent { }
 
class Brother extends Parent { }
 
 
public class Polymorphism01 {
 
    public static void main(String[] args) {
 
        Parent p = new Parent();
 
        System.out.println(p instanceof Object); // true
        System.out.println(p instanceof Parent); // true
        System.out.println(p instanceof Child);  // false
        System.out.println();
 
        Parent c = new Child();
 
        System.out.println(c instanceof Object); // true
        System.out.println(c instanceof Parent); // true
        System.out.println(c instanceof Child);  // true
    }
}
cs

 

[예제1] 아래 코드를 보며 Shape 클래스의 다형성을 확인해 보세요.

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
class Shape {
    public void what() {
        System.out.print("Shape ");
    }
}
 
class Rectangle extends Shape {
    public void what() {
        System.out.print("Rectangle ");
    }
}
 
class Square extends Rectangle { }
 
class Oval extends Shape {
    public void what() {
        System.out.print("Oval ");
    }
}
 
class Circle extends Oval {
    public void what() {
        System.out.print("Circle ");
    }
}
 
public class Main {
  public static void main(String[] args) {
    // example of array: int[] a = {1,2,3};
    Shape[] shapes = {new Shape(), new Rectangle(), new Square(), new Circle()};
 
    for (Shape s : shapes) {
        s.what();
    }
    /* same result above
    for(int i = 0; i < shapes.length; i++) {
      shapes[i].what();
    }
    */
  }
}
 
cs

 

[실행결과]

  • Shape object는 "Shape"을 출력한다.
  • Rectangle object는 "Rectangle"을 출력한다.
  • Square object는 오버라이드(Override)된 what 메소드가 없어서, 부모 클래스인 Rectangle의 what 메소드를 실행해 "Rectangle"을 출력한다.
  • Circle object는 "Circle"을 출력한다.

 

 

[유제1] 아래 코드의 출력결과를 말해 보세요.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Student {
    public String getFood() {
        return "Pizza";
    }
 
    public String getInfo() {
        return this.getFood();
    }
}
 
class GradStudent extends Student {
    public String getFood() {
        return "Taco";
    }
}
 
public class Main {
  public static void main(String[] args) {
    Student s1 = new GradStudent();
    System.out.println(s1.getInfo());
  }
}
cs

 

 

 

[유제2] 아래 코드의 실행 결과를 말해보자.

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
class Car {
    private int fuel;
    public Car() {
        fuel = 0;
    }
    
    public Car(int g) {
        fuel = g;
    }
 
    public void addFuel() {
        fuel++;
    }
 
    public void display() {
        System.out.print(fuel + " ");
    }
}
 
class RaceCar extends Car {
    public RaceCar(int g) {
        super(2 * g);
    }
}
 
public class Main {
  public static void main(String[] args) {
    Car car = new Car(5);
    Car fastCar = new RaceCar(5);
    car.display();
    car.addFuel();
    car.display();
    fastCar.display();
    fastCar.addFuel();
    fastCar.display();
  }
}
cs

 


 

[예제2] 아래 코드에서 Book b = new Dictionary() 이 실행된다고 가정할 때, compile-time에서 에러가 발생 할 수 있는 명령어는 아래 A, B, C 중 어떤것일까?

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class Book
{
    public String getISBN()
    {
        // implementation not shown
    }
 
    // constructors, fields, and other methods not shown
}
 
public class Dictionary extends Book
{
    public String getDefinition()
    {
        // implementation not shown
    }
}
cs

 

  • 정답: B
  • 이유: compile-time에서는 Book 클래스 타입의 변수 b가 선언만 되었기 때문에, Book 클래스가 getDefinition()이라는 메소드를 가지고 있지도 않고 또한 그런 메소드를 상속 조차 받은것도 아니므로, compile-time에서 B는 에러가 발생합니다.
  • run-time에서는 b object가 실제로 만들어지기 때문에 b.getDefinition()이 에러없이 잘 실행됩니다.C는, b object를 Dictionary 클래스 타입으로 변경(casting)하여, Dictionary 클래서는 getDefinition()메소드가 존재하기 때문에 에러가 발생하지 않습니다.
1.declared type - The type that was used in the declaration. List aList = new ArrayList() has a declared type of List. This is used at compile-time to check that the object has the methods that are being used in the code.
2.run-time type - The type of the class that created the object. List aList = new ArrayList() has a run-time type of ArrayList. This is used at run-time to find the method to execute.

 

 

 


[유제 정답은 아래 "더보기" 클릭]

더보기

[유제1 정답]

(이유: 만들어진 s1 object는 GradStudent 클래스의 object이므로, "this.getFood() "의 this는 GradStudent의 object 이므로, "Taco" 가 출력된다.)

 

 

[유제2 정답]

(이유: Car 클래스를 RaceCar 클래스가 상속 받았기 때문에, RaceCar의 object는 Car 클래스의 public 메소드를 모두 사용할 수 있다.)

 

 

 

 


 

 

728x90
반응형

댓글