천천히 빛나는

Java 기초 : 다형성 (Polymorphism) 본문

JAVA/JAVA

Java 기초 : 다형성 (Polymorphism)

까만콩 •ᴥ• 2024. 1. 17. 23:07

 

다형성 (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(); // 오류 발생
    }
}

부모 클래스 A, 자식 클래스 B가 있을 때 클래스 B의 객체를 A에 담을 수가 있다. 하지만 클래스 B의 메소드 y를 실행할 수는 없다. 클래스 B의 데이터 형을 클래스 A로 하면 클래스 B는 마치 클래스 A인것처럼 동작하게 된다. 

 

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());
    }
}

하지만 위 코드에서는 오버라이딩된 함수의 결과값이 출력된다. 즉 B.x가 출력되는 것이다. 

 

- 새롭게 추가한 함수는 실행 불가

- 상위 클래스의 함수를 오버라이딩 했다면 그 값이 출력

 

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());
    }
}

그렇다면 위 코드는 어떤 값이 출력될까? 정답은 B.x와 B2.x이다. 

데이터 타입이 부모 클래스이기 때문에 부모클래스에서 정의한 함수만 호출할 수가 있다. 하지만 하위 클래스에서 오버라이딩 했다면 그 함수가 출력되게 된다. 하위 클래스에서 추가한 메소드들은 호출할 수 없다.

 

abstract class Calculator{
    int left, right;
    public void setOprands(int left, int right){
        this.left = left;
        this.right = right;
    } 
    int _sum() {
        return this.left + this.right;
    }
    public abstract void sum();  
    public abstract void avg();
    public void run(){
        sum();
        avg();
    }
}
class CalculatorDecoPlus extends Calculator {
    public void sum(){
        System.out.println("+ sum :"+_sum());
    }
    public void avg(){
        System.out.println("+ avg :"+(this.left+this.right)/2);
    }
} 
class CalculatorDecoMinus extends Calculator {
    public void sum(){
        System.out.println("- sum :"+_sum());
    }
    public void avg(){
        System.out.println("- avg :"+(this.left+this.right)/2);
    }
} 
public class CalculatorDemo {
    public static void main(String[] args) { 
        Calculator c1 = new CalculatorDecoPlus();
        c1.setOprands(10, 20);
        c1.run();
         
        Calculator c2 = new CalculatorDecoMinus();
        c2.setOprands(10, 20);
        c2.run();
    }
   
}

CalculatorDecoPlus c1 = new CalculatorDecoPlus();가 아니라 Calculator c1 = new CalculatorDecoPlus();라고 되어있는 부분을 주목하면 된다. c1.run()과 c2.run()은 다른 값을 출력하게 된다.

 

 

인터페이스와 다형성

interface I{}
class C implements I{}
public class PolymorphismDemo2 {
    public static void main(String[] args) {
        I obj = new C();
    }
}

특정한 인터페이스를 구현하고 있는 클래스가 있을 때 이 클래스의 데이터 타입으로 인터페이스를 지정할 수 있다.

 

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();
    }
}

인터페이스 I2는 메소드 A만을 정의하고 있고 I2를 데이터 타입으로 하는 인스턴스는 마치 메소드 A만을 가지고 있는 것처럼 동작한다.

 

 

 

 

클래스 형변환

public class Rabbit extends Animal {
    public Rabbit() {
        super();
    }
    @Override
    public void eat() {
        System.out.println("토끼가 풀을 뜯어 먹습니다");
    }
    
    public void jump() {
        System.out.println("토끼가 깡총깡총 뜁니다");
    }
}

Animal을 상속받는 Rabbit이 있다.

 

Animal a1 = new Rabbit();
a1.jump(); // 오류남
((Rabbit)a1).jump(); // 클래스 형변환 -> 런타임 오류

a1으로 jump()를 실행하려면 Rabbit으로 다운캐스팅을 해줘야 한다. 하지만 (Rabbit)과 같이 작성해줘도 런타임 오류가 난다.

if(a1 instanceof Rabbit) {
        ((Rabbit)a1).jump();
}

이와 같이 instance of을 통해 해당 타입이 맞는지 확인하고 다운캐스팅이 가능하다.