JAVA_Polymorphism

0ne·2024년 6월 16일
0

java

목록 보기
10/11

binding

정의 : method invocation과 method definition을 연결하는 과정

early binding; static binding

컴파일 시점에 메서드 정의와 메서드 호출을 연결하는 방식

  • 정적 메서드 호출; 클래스명이 아닌 심지어 객체를 통해 호출할 때도
    * method hiding이 일어나는 경우 : 참조된 타입에 의해 결정(late binding이랑 반대)
  • 오버로딩
    * 컴파일러는 각 메서드 호출에 대해 올바른 메서드 정의를 찾고, 이를 컴파일 시점에 결정하므로

late binding; dynamic binding

실행 시점에 메서드 정의와 메서드 호출을 연결하는 방식 (동적 바인딩)

  • 오버라이딩 (다형성)
  • java는 기본적으로 late binding을 사용

polymorphism

정의

("다양한 형태") 한 객체가 여러 타입의 객체로 취급될 수 있는 것

다형적 참조 + 메서드 overriding 으로 구현되는 것

핵심 2가지

1. 부모는 자식을 품을 수 있다.

Parent poly = new Parent()
Parent poly = new Child()
Parent poly = new GrandChild()

상속관계는 부모 방향으로 찾아 올라갈 수는 있으나 자식방향으로 찾아 내려갈 수는 없다.
poly.childMethod() : ㄴㄴ (캐스팅 필요)

2. overriding 된 메서드가 항상 우선권을 가짐

객체 자체가 사실상 자식 인스턴스도 포함할 때, 그 자식에서 override된 메서드로 작동한다; 실행 시점에 객체의 실제 타입에 따라 호출되는 메서드가 결정

public class OverridingMain {
    public static void main(String[] args) {
//자식 변수가 자식 인스턴스 참조
		Child child = new Child();
		System.out.println("Child -> Child"); 
		System.out.println("value = " + child.value);//value = child
		child.method(); //childmethod
//부모 변수가 부모 인스턴스 참조
		Parent parent = new Parent(); 
		System.out.println("Parent -> Parent"); 
		System.out.println("value = " + parent.value);//value = 
		parent.method(); //parentmethod
//부모 변수가 자식 인스턴스 참조(다형적 참조)
		Parent poly = new Child();
		System.out.println("Parent -> Child"); 
		System.out.println("value = " + poly.value); //변수는 오버라이딩X value = parent
		poly.method(); //메서드 오버라이딩! : childmethod

casting

1) downcasting : (원래 부모 타입인 걸) 자식타입으로 변경

	Parent poly1 = new Child();
	Parent poly2 = new Parent();
	Child child1 = (Child) poly1; 
	Child child2 = (Child) poly2;  //runtime error
	child1.childMethod(); ((Child) poly1).childMethod();
	child2.childMethod() //execute impossible
  • poly의 타입은 그래로! 자바에선 항상 value-copy!!

  • poly와 child모두 같은 참조값(e.g. x001)을 참조함

  • 재할당외에는 변수의 내용을 바꿀수 있는 게 없음

  • memory상에 자식이 존재하지 않는 경우, 실패하는 문제가 생성된다. casting 표시를 항상 해줘야 한다.

2) upcasting : 자식타입을 부모타입으로 변경

	Child child = new Child();
	Parent parent1 = (Parent) child; //캐스팅 표시
	Parent parent2 = child; //권장
  • 객체를 생성하면 해당 타입의 부모타입은 항상 생성되는데,

  • memory상에 부모가 존재하므로 upcasting은 항상 성공하고, casting 생략도 가능

  • 업캐스팅된 객체는 부모 클래스 타입으로만 참조되기 때문에, 자식 클래스에서 정의된 메서드나 필드에는 접근할 수 없다

instanceOf의 사용

public class CastingMain5 {
	public static void main(String[] args) { 
	Parent parent1 = new Parent(); 
	System.out.println("parent1 호출"); 
	call(parent1); // if false
	Parent parent2 = new Child();
	System.out.println("parent2 호출"); 
	call(parent2); //if true
}
     private static void call(Parent parent) {
         parent.parentMethod();
         if (parent instanceof Child) {
			System.out.println("Child 인스턴스 맞음"); 
			Child child = (Child) parent; 
			child.childMethod();
		}
	}
}

주의

static method에는 다형성이 적용되지 않는다

class Parent {
    public static void staticMethod() {
        System.out.println("Parent static method");
    }

    public void instanceMethod() {
        System.out.println("Parent instance method");
    }
}

class Child extends Parent {
    public static void staticMethod() {
        System.out.println("Child static method");
    }

    @Override
    public void instanceMethod() {
        System.out.println("Child instance method");
    }
}

public class Main {
    public static void main(String[] args) {
        Parent parent = new Child();

        // 정적 메서드 호출
        parent.staticMethod(); // Parent static method 출력
        Parent.staticMethod(); // Parent static method 출력
        Child.staticMethod();  // Child static method 출력

        // 인스턴스 메서드 호출 (다형성 적용)
        parent.instanceMethod(); // Child instance method 출력
    }
}

final 메소드는 애초에 override가 안되니까 당연히 다형성을 적용할 수 없다.

적용

[toString()과 다형성]

class Parent {
    @Override
    public String toString() {
        return "This is a Parent object";
    }
}

class Child extends Parent {
    @Override
    public String toString() {
        return "This is a Child object";
    }
}

public class Main {
    public static void main(String[] args) {
        Parent parent = new Parent();
        Parent child = new Child(); // 업캐스팅

        // toString() 메서드 호출
        System.out.println(parent.toString()); // This is a Parent object
        System.out.println(child.toString());  // This is a Child object
    }
}

[clone()]

class Parent {
    private int value;

    public Parent(int value) {
        this.value = value;
    }

    // 복사 생성자
    public Parent(Parent other) {
        this.value = other.value;
    }

    public Parent clone() {
        return new Parent(this); // 복사 생성자를 사용하여 객체 복사
    }

    @Override
    public String toString() {
        return "Parent{" + "value=" + value + '}';
    }
}
  • 참고로 접근 제어자가 protected에서 public으로 확장되었다.
class Child extends Parent {
    private int extraValue;

    public Child(int value, int extraValue) {
        super(value);
        this.extraValue = extraValue;
    }

    // 복사 생성자
    public Child(Child other) {
        super(other); // 부모 클래스의 복사 생성자 호출
        this.extraValue = other.extraValue;
    }

    @Override
    public Child clone() {
        return new Child(this); // 복사 생성자를 사용하여 객체 복사
    }

    @Override
    public String toString() {
        return "Child{" + "value=" + super.toString() + ", extraValue=" + extraValue + '}';
    }
}

원래 clone()의 리턴타입은 Object이다. 그런데 리턴타입을 Child로 바뀌었다. 이를 오버라이딩이라 할 수 있는가?

  • override의 예외라 봐야 한다.
  • covariant return type은 자바에서 오버라이딩할 때 리턴 타입을 부모 클래스 메서드의 리턴 타입의 하위 타입으로 변경할 수 있게 해주는 예외적인 규칙
public class Main {
    public static void main(String[] args) {
        Child original = new Child(10, 20);
        Child copy = original.clone(); // 복사 생성자를 사용하여 객체 복사

        System.out.println("Original: " + original);
        System.out.println("Copy: " + copy);
    }
}
  • 기본적으로 얕은 복사만 수행하는 clone()을 깊은 복사를 수행하도록 override할 수 있다.
  • 다형성을 이용하여 실제 타입에 맞는 재정의된 메서드를 사용할 수 있게 되었다.
profile
@Hanyang univ(seoul). CSE

0개의 댓글