다형성

·2025년 4월 19일

Power Java

목록 보기
28/75

6.5 다형성

다형성
: 일반적으로 객체지향 프로그래밍에서 다형성이란 객체들이 똑같은 메시지를 받더라도 각자의 실제 타입에 따라서 서로 다른 동작을 하는 것을 말한다.

-메소드 오버로딩
-메소드 오버라이딩
-제네릭 프로그래밍
을 포함한다.

업캐스팅
: 부모 클래스 변수로 자식 클래스 객체를 참조하는 것 (=상형 형변환)

public class ShapeTest {
	public static void main(String[] args) {
    	Shape s1, s2;
        
    	s1 = new Shape();		// 당연하다.
    	s2 = new Rectangle();	// Rectangle 객체를 Shape 변수로 가리킬 수 있다.
 	}
}

=> 자식 클래스 객체는 부모 클래스 객체를 포함하고 있기 때문에 위 문장은 적법하다.

Q. 업캐스팅을 사용했을 때 해당 부모 참조 변수를 통하여 자식 클래스의 모든 필드와 메소드를 사용할 수 있는가 ?
X
=> 상속받은 부분만을 사용할 수 있다.
참조 변수의 타입이 Shape(부모 클래스)이기 때문이다.
*이는 곧 어떤 멤버를 사용할 수 있느냐는 변수의 타입에 의하여 결정된다는 것을 말한다.

< 업캐스팅 vs 다운캐스팅 >
•업캐스팅
자식 클래스 객체 -> 부모 클래스 객체 (부모 변수로 자식 객체를 가리킴)
-묵시적 수행 가능
-부모 클래스의 멤버에 접근 가능

•다운캐스팅
부모 클래스 객체 -> 자식 클래스 객체 (자식 변수로 부모 객체를 가리킴)
-명시적으로 하여야 함
-업캐스팅이 이루어지면 다시 다운캐스팅을 통해 원래의 클래스(자식)으로 돌아오도록 한다.

class Parent {
	void print()	{	System.out.println("Parent 메소드 호출");	}
    
class Child extends Parent {
	@Override void print()	{	System.out.println("Child 메소드 호출");	}
}

public class Casting {
	public static void main(String[] args)	{
    	Parent p = new Child();
        // 업캐스팅
        // 자식 객체를 부모 객체로 형변환 (타입 변화 자식->부모)
        p.print();
        
        // Child c = new Parent();	// 컴파일 오류
        
        child c = (Child)p;
        // 다운캐스팅
        // 부모 객체를 자식 객체로 형변환
        
        c.print();
       	// 메소드 오버라이딩-자식 객체의 메소드 호출 
    }
}
// 실행 결과
Child 메소드 호출
Child 메소드 호출

< 동적 바인딩 >
: 오버라이딩된 메소드를 호출할 때, 참조 변수의 타입이 아니라 실제 객체의 타입을 기준으로 어떤 메소드를 실행할지 결정하는 것.

•바인딩
: 메소드의 호출을 실제 메소드의 몸체와 연결하는 것.
C언어에서는 컴파일 단계에서 모든 바인딩이 완료되지만(정적)
자바 가상 머신은 실행 단계에서 변수가 참조하는 객체의 실제 타입을 보고 적절한 메소드를 호출하게 된다(동적)

*오버라이딩이 불가능한 메소드는
static, final, private가 붙은 정적 타입의 메소드
객체의 실제 타입과 상관없이 정적 바인딩이 일어난다.
(=>컴파일 타임에 결정)

< 업캐스팅의 활용 >
-메소드의 매개 변수를 부모 타입으로 선언하면 훨씬 넓은 범위의 객체를 받을 수 있다.

ex) 메소드의 매개 변수를 Rectangle 타입으로 선언하지 않고 Shape 타입으로 선언하면 Shape에서 파생된 모든 타입의 객체를 받을 수 있게 된다.

public class ShapeTest {
	public static void print(Shape obj) {
    	System.out.println("x=" + s.x + " y=" + s.y);
    }
    
    public static void main(String arg[]) {
    	Rectangle s1 = new Rectangle();
        Tringle s2 = new Triangle();
        Circle s3 = new Circle();
        
        print(s1);
        print(s2);
        print(s3);
    }
}

=>
(Shape obj) Shape에서 파생된 모든 클래스의 객체를 다 받을 수 있다.
print(s1);을 하면 부모 클래스 Shape의 자식 클래스의 객체인 s1을 받아서 메소드를 실행한다.

Q. 이때 자바에서 생성되는 모든 객체를 전부 전달받을 수 있는 메소드를 선언하려면?
A. 모든 객체는 Object 클래스를 상속받기 때문에 아래와 같이 정의하면 된다.

public static void print(object obj) {
	...
}

instanceof 연산자
: 동적 바인딩으로 인해 변수의 타입만 보고는 변수가 가리키는 실제 타입을 알 수 없다.
변수가 가리키는 객체의 실제 타입을 알고 싶을 땐 해당 연산자를 사용한다.

if (obj instanceof Rectangle) { ... }

obj라는 참조 변수가 현재 Rectangle 객체를 참조하고 있다면
해당 수식(obj instanceof Rectangle)이 true를 반환한다.

< 종단 클래스와 종단 메소드 >
•종단 클래스
: 상속을 시킬 수 없는 클래스
-> 보안상의 이유로 필요하다.
ex) String 클래스

-종단 클래스로 선언하려면 클래스의 선언 앞에 final을 붙인다.

final class String {
	...
}

이렇게 종단 클래스가 아닌, 일반 클래스에서 특정한 메소드만 재정의 될 수 없게 만들려면 종단 메소드로 선언하면 된다.

class Baduk {
	enum BadukPlayer { WHITE, BLACK }
    ...
    final BadukPlayer getFirstPlayer() {
    	return BadukPlayer.BLACK;
    }
}

*대개 생성자에서 호출되는 메소드들은 일반적으로 final로 선언된다.
슈퍼 클래스에서 종단 메소드로 선언된 메소드는 서브 클래스에서 대치될 수 없다.
반면 추상 메소드는 사요되기 전에 반드시 대치되어야만 한다.

0개의 댓글