본문 바로가기
생활코딩/JAVA

생활코딩 - JAVA (다형성)

by Love of fate 2020. 12. 29.
728x90
반응형

[다형성]

다형성은 한 메소드나 클래스가 다양한 방식으로 동작하는 것을 의미한다. 

ex) 

a()라는 메소드가 있을 때 이 메소드가 사왕에 따라 다르게 동작한다는 것을 의미한다. 또한 A라는 클래스가 있을 때 마찬가지로 어떤 경우에는 똑같은 크래스지만 그 클래스의 메소드를 실행했을 때 상황에 따라 다양하게 동작하는 것이 바로 다형성이다.

 

다형성은 객체나 인터페이스, 추상화처럼 뭔가 철학적인 느낌을 자야낸다. 

다형성의 가장 쉬운 예로 오버로딩을 들 수 있다. 오버로딩은 이름이 같지만 매개변수 형식에 따라 다른 베소드가 호출되는 기법이기 때문이다. 즉, 이름은 같지만 다르게 동작한다고 할 수 있기 때문에 다형성이라는 측면에서 생각해 볼 수 있다. 

 

[클래스와 다형성1]

다형성을 통해 메소드나 클래스를 바라보는 시야를 넓히는 것이 중요하다.

 

[다형성 예제]

1
2
3
4
5
6
7
8
package org.opentutorials.javatutorials.polymorphism;
class A{}
class B extends A{}
public class PolymorphismDemo1 {
    public static void main(String[] args) {
        A obj = new B();
    }
}

B클래스는 아주 간단한 형태의 클래스이다.

B클래스는 A클래스를 상속하고 있다. main메소드에서는 B클래스를 인스턴스화해서 obj 변수에 담았다. obj 변수는 A 클래스를 데이터 타입으로 지정했다. 즉, B 클래스로 인스턴스를 만들었지만 해당 인스턴스는 A 클래스의 데이터 타입 행세를 하고 있다는 것이다.

 

[A 타입의 B 인스턴스를 생성 후 메소드를 호출]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package org.opentutorials.javatutorials.polymorphism;
class A{
    public String x(){return "x";}
}
class B extends A{
    public String y(){return "y";}
}
public class PolymorphismDemo1 {
    public static void main(String[] args) {
        A obj = new B();
        obj.x();
        obj.y();
    }
}

실행하면 오류가 발생한다. 그 이유는 obj.y(); 때문이다.

- The method y() is undefined for the type A

 

obj 변수는  실질적으로는 B 클래스의 인스턴스를 담고 있지만 A 클래스 행세를 하고 있다. A 클래스에는 x() 메소드가 들어 있기 때문에 obj.x(); 는 정상적으로 실행된다. 그런데 obj.y() 는 실행되지 않는다.

obj 안에 B 클래스의 인스턴스를 담았지만 obj 변수는 A 클래스 행세를 하고 있고, A 클래스에는 y() 메소드가 정의돼 있지 않다.  그렇기 때문에 obj 변수에는 y() 메소드가 존재하지 않는 상태인 것이다. 

 

[B 클래스에서 x() 메소드를 오버라이딩]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package org.opentutorials.javatutorials.polymorphism;
class A{
    public String x(){return "A.x";}
}
class B extends A{
    public String x(){return "B.x";}
    public String y(){return "y";}
}
public class PolymorphismDemo1 {
    public static void main(String[] args) {
        A obj = new B();
        System.out.println(obj.x());
    }
}

클래스 A의 메소드 x를 클래스 B에서 오버라이딩하고 있다. 실행 결과는 아래와 같다.

1

B.x

클래스 B를 클래스 A의 데이터 타입으로 인스턴스화 했을 때 클래스 A에 존재하는 맴버만이 클래스 B의 맴버가 된다. 동시에 클래스 B에서 오버라이딩한 맴버의 동작방식은 그대로 유지한다. 

 

메소드를 오버라이딩하면 오버라이딩한 메소드의 우선순위가 더 높아진다. 

실행하는 부분에서는 B클래스를 인스턴스화해서 A 클래스 타입의 변수를 obj에 담았다. 이상태에서 obj.x()를 실행하면 반반환된 결과를 System.out.println을 통해 화면에 출력한다.

 

어떤 인스턴스가 A 클래스 행세를 한다는 것, B 클래스의 인스턴스가 A 클래스 행세를 한다는 것은 A 클래스에 정의된 메소드만을 실행할 수 있고, B 클래스에서 추가로 정의한 메소드는 실행할 수 없음을 의미한다.

하지만 B 클래스에 있는 어떤 특정 메소드가 상위 클래스의 메소드를 오버라이딩했다면 실제로 실행되는 메소드는 인스턴스화된 클래스의 메소드가 되는 것이다.

 

[클래스와 다형성 2]

[B 클래스와 B2 클래스에서 A 클래스를 상속]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package org.opentutorials.javatutorials.polymorphism;
class A{
    public String x(){return "A.x";}
}
class B extends A{
    public String x(){return "B.x";}
    public String y(){return "y";}
}
class B2 extends A{
    public String x(){return "B2.x";}
}
public class PolymorphismDemo1 {
    public static void main(String[] args) {
        A obj = new B();
        A obj2 = new B2();
        System.out.println(obj.x());
        System.out.println(obj2.x());
    }
}

[출력 결과]

1

2

B.x

B2.x

B 클래스를 인스턴스화한 obj가 있다. 데이터 타입은 B 클래스의 부모인 A클래스이다.

B2클래스를 인스턴스화한 obj가 있는데, B2 클래스의 부모는 A 클래스이다. 즉, A 클래스의 자식클래스인 B 클래스와 B2클래스를 인스턴스화해서 데이터 타입이 모두 A 클래스인 obj와 obj2를 만든 것이다. 

obj와 obj2 인스턴스에 담긴 실제 타입은 B와 B2 클래스이다. 하지만 두 클래스의 타입은 모두 A 클래스를 부모 클래스로 지정해서 부모 행세를 하고 있기 때문에 부모 클래스에서 정의한 메소드만 호출할 수 있다. 이것은 자식 클래스가 부모 행세를 하고 있기 때문에 자식 클래스에 추가한 메소드는 이를 사용하는 입장에서 존재하지 않기 때문이다. 

 

그러면서도 부모 클래스의 메소드를 자식 클래스에서 오버라읻ㅇ 했다면 실제 동작 방식은 부모 클래스의 메소드의 동작방식에 따라 "A.x"를 반환하는 것이 아니라 각 자식 클래스에서 오버라이딩한 메소드의 내용이 실행된다. 

서로 다른 객체지만 동일한 데이터 타입으로 존재하면서 각 클래스에 정의돼 있는 메소드를 호출할 때는 각 클래스의 메소드에 정의된 방식대로 동작한다는 것이 다형성이다. 

 

메소드, 객체, 반복문이라는 주제는 필수적인 내용이고, 다형성은 지금 바로 알면 너무 좋고 모르면 언젠가 알게 될 것이라고 생각하면 된다. 

 

[인터페이스와 다형성 1]

인터페이스는 다형성을 구현하는 데 아주 중요한 도구이다. 하위 클래스를 인스턴스화할 때 그것의 데이터 타입으로 상위 클래스를 지정할 수 있었다. 이와 마찬가지로 어떤 인터페이스를 만들었는데 그 인터페이스를 어떤 클래스가 구현했다고 가정할 때 클래스를 인스턴스화할 때 해당 인스턴스를 담고 잇는 변수의 데이터 타입으로 그 클래스가 구현하고 있는 인터페이스를 지정할 수 있다.

 

[변수의 데이터 타입으로 인터페이스를 지정]

1
2
3
4
5
6
7
8
package org.opentutorials.javatutorials.polymorphism;
interface I{}
class C implements I{}
public class PolymorphismDemo2 {
    public static void main(String[] args) {
        I obj = new C();
    }
}

 C클래스를 인스턴스화해서 obj 변수에 담고 있다. obj 변수의 데이터 타입은 현재 I 인페이스로 지정돼 있다.

C클래스와 인스턴스의 데이터 타입인 I는 서로 다르다. 

 

C 클래스는 I 인터페이스를 구현한다. 

* 어떤 클래스가 어떤 인터페이스를 구현하고 있다면 그 클래스의 인스턴스는 그 인터페이스일 수 있다는 것이다.

C 클래스가 인스턴스화될 때 데이터 타입이 I인 경우 이유는 C 클래스가 I 인터페이스를 구현하고 있기 때문이다. 

 

 

[D 클래스에서 I2, I3 인터페이스를 구현]

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
package org.opentutorials.javatutorials.polymorphism;
interface I2{
    public String A();
}
interface I3{
    public String B();
}
class D implements I2, I3{
    public String A(){
        return "A";
    }
    public String B(){
        return "B";
    }
}
public class PolymorphismDemo3 {
    public static void main(String[] args) {
        D obj = new D();
        I2 objI2 = new D();
        I3 objI3 = new D();
         
        obj.A();
        obj.B();
         
        objI2.A();
        //objI2.B();
         
        //objI3.A();
        objI3.B();
    }
}

objI2의 경우에는 데이터 타입이 I2이다. 즉, objI2는 D 클래스의 인스턴스임에도 마치 자신이 I2 인터페이스인 것처럼 행세하는 것이다. 

어떤 클래스를 사용할 때 해당 클래스가 가진 모든 기능을 사용해야 한다면 당연히 데이터 타입을 그 클래스의 데이터 타입으로 지정해야겠지만 어떤 클래스를 사용할 때 그 클래스가 가진 여러 가지 기능 중에서 특정 인터페이스에 해당하는기능만 사용한다면 데이터 타입으로 인터페이스를 지정해서 그 인터페이스에서 정의한 멤버를 제외한 나머지는 마치 존재하지 않는 것처럼 만들어 이 클래스를 사용하는 사람이 신경 쓰지 않게 할 수 있다.

한 인터페이스가 있고 그 인터페이스를 구현하는 클래스가 여러 개 있다면 각 클래스를 인스턴스화할 때 데이터 타입으로 각 클래스가 공통적으로 구현하는 인터페이스를 지정함으로써 데이터 타입은 같지만 실제 클래스가 무엇이느냐에 따라 다르게 동작하는 다형성을 구현할 수 있다는 것도 이해할 수 있을 것이다. 이것이 바로 인터페이스와 다형성 간의 관계이다.

 

728x90
반응형

'생활코딩 > JAVA' 카테고리의 다른 글

생활코딩 - JAVA (예외 던지기)  (0) 2020.12.30
생활코딩 - JAVA (예외 (exception))  (0) 2020.12.29
생활코딩 - JAVA (인터페이스)  (0) 2020.12.19
생활코딩 - JAVA (final)  (0) 2020.12.19
생활코딩 - JAVA (Abstract)  (0) 2020.12.19