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로 선언된다.
슈퍼 클래스에서 종단 메소드로 선언된 메소드는 서브 클래스에서 대치될 수 없다.
반면 추상 메소드는 사요되기 전에 반드시 대치되어야만 한다.