다형성
- 객체지향 개념에서 다형성이란 여러가지 형태를 가질 수 있는 능력을 말함
- 타입 캐스팅 혹은 메소드의 오버로딩 및 오버라이딩으로 다형성 구현
- 타입 캐스팅을 통한 다형성 구현은 실제 객체를 변경하는게 아니라 참조타입만 바꾸는 것
자동 형변환을 통한 타입 다형성
- 형변환에서의 크다는 메모리의 크기가 아닌 범위를 뜻함 → Object 클래스는 모든 클래스의 슈퍼클래스 이므로 범위가 가장 큼
- 범위가 넓은 레퍼런스 혹은 기본형 변수로 더 범위가 좁은 객체 또는 기본형 타입을 참조할 수 있다
- 이를 up-casting이라고하며 up-casting은 명시적으로 형변환하지 않아도 자동형변환 된다.
Primitive type
- primitive type은 double이 표현할 수 있는 범위가 가장 크고 byte가 가장 작음
- byte → double 까지 자동형변환 되므로, 메서드 파라미터에서 double형으로 모든 primitive 타입을 받아 캐스팅하여 사용 가능
- boolean type은 형변환이 불가능
Reference type
- 슈퍼클래스 타입으로 서브클래스의 객체를 레퍼런싱 가능
Object o = new String();
→ 모든 클래스는 Object 클래스를 상속하므로 가능
- 이 때 서브클래스의 인스턴스는 업캐스팅을 통한 자동형변환(비 명시적)
- 서브클래스 인스턴스를 참조하는 슈퍼클래스는 서브클래스의 멤버에 접근할 수 없음
- 서브클래스의 객체에 실제로 멤버가 존재하나 슈퍼클래스에 존재하지 않는 멤버는 참조 불가능 → Shadow impact
- 예외적으로 오버라이딩 된 메소드는 재정의된 것으로 슈퍼클래스 메소드 대신 자식 메소드를 호출
- Dynamic Linking
- ex) 매개변수를 object로 받고 하위 메소드로 값을 비교하는 경우가 가능
class A{}
class B extends A{ String toString(){}}
class C extends B{}
Object x = new B();
B.toString()
Object x = new A();
A.toString()
B x = new Object();
B.toString();
A x = new C();
C.toString();
- 레퍼런스의 선언된 클래스의 메소드만 호출 가능하다 → 하위 객체로 생성되어 오버라이딩으로 재정의 되었다면 하위 객체의 메소드를 호출한다
여러 상황에서 Reference Casting
- 매개변수 자동형변환
- 메소드를 파라미터의 타입 별로 오버로딩하는 대신 파라미터를 Object 타입으로 받아서 동작할 수 있음
- 배열 및 컬렉션 자동형변환
- 슈퍼클래스 타입으로 배열과 컬렉션 선언 시 서브클래스의 객체를 담을 수 있음.
슈퍼클래스와 서브클래스의 멤버 변수
- 슈퍼클래스와 서브클래스에서 같은 이름으로 멤버변수를 선언했더라도 호출할 때는 레퍼런스 타입에 정의된 멤버 변수를 호출한다.
- super와 this로 멤버 변수를 호출할 클래스를 정의
명시적 형변환
- 자동 형변환이 되지 않는 다운 캐스팅
- 실제로 참조하고 있는 객체로만 가능
- 서브클래스가 슈퍼클래스를 참조하거나 상속관계가 아닌 클래스를 참조할 수 없음
- 에러 : ClassCastException(런타임 에러)
- 문법적으로 super클래스의 sub클래스의 형변환은 가능하지만 실제 sub클래스가 참조할 수 없는 메모리에 접근하므로 런타임 에러
- Shadow Impact를 해결하기 위해 업캐스팅한 레퍼런스를 되돌려줄 때 사용
class Car{
void run(){
System.out.println("run");
}
}
class PoliceCar extends car{
@Override
void run(){
System.out.println("run with siren");
}
void siren(){
System.out.println("siren");
}
}
public class Main{
public static void main(String[] args){
Car myCar = new PoliceCar();
PoliceCar errorCar = new Car();
myCar.run();
myCar.siren()
((PoliceCar)myCar).siren();
}
}
instanceof
A instanceof B
A의 객체를 B가 참조할 수 있는가? (형변환이 가능한가)
- 즉 A가 B의 인스턴스인가?
- B가 A의 슈퍼클래스일 경우 혹은 B와 A가 같은 클래스일 경우 true
- ex)
클래스 instanceof Object
는 항상 true
메소드 다형성
- 오버로딩
- 같은 클래스 내에 같은 이름을 갖는 메소드를 파라미터를 다르게 하여 별개의 메소드로 선언
- 새로운 메소드를 만드는 것
- 오버라이딩
- 상속 관계에서 슈퍼클래스의 메소드를 서브클래스에서 재정의
- 기존의 메소드를 덮어쓰는 것(재정의)
- 파라미터와 반환값을 포함한 내용이 모두 같아야함
- 오버라이드 어노테이션
@Override
- 명시적으로 오버라이딩을 했다는 것을 작성하여, 가독성을 확보하고 컴파일 시 슈퍼클래스 메소드가 변화하더라도 컴파일러가 인지할 수 있도록 함
- 메소드 시그니처
- 자바 컴파일러는 메소드 시그니처( 메소드명과 파라미터)를 통해 같은 메소드인지 판단함.
- 반환타입과 exeption은 메소드 시그니처 고려대상이 아님.
void myMethod(int a){}
int myMethod(int a) throws IOException{}
오버라이딩 - Dynamic Linking(Virtual Invocation)
-
다형성 관계에서 overriding 된 메소드가 호출
-
상속 관계에서 기존의 코드를 수정하지 않고 추가된 메소드를 반영
-
static 메소드는 오버라이딩을 허용하지 않음
- 클래스의 상속은 객체 생성 시에 이루어지므로 클래스 로딩시에 정의되는 스태틱 메소드에 오버라이딩읠 정의 할 수 없음
- 서브클래스에 같은 스태틱 메서드를 재정의하는 것을 hiding이라고 함
-
슈퍼클래스에 선언된 메소드가 원하는 기능에 따라 각각의 서브클래스들에서 다른 동작을 통한 같은 기능을 하도록 오버라이딩
- ex)
servlet.service()
→ 서브클래스에서 각각 다 다른 service 수행하고 같은 결과를 반환하게 함
-
업캐스팅된 레퍼런스에서 오버라이딩하기 전 슈퍼클래스의 메소드에 접근할 수 있을까?
- Dynamic Linking을 통해 자동으로 자식 객체의 메서드를 호출하게 됨.
- 호출하기 위해서는 오버라이딩 된 메소드에서 super().메소드를 해주는 객체를 재정의해야함
- 혹은 접근제어자로 오버라이딩 된 메소드의 접근을 제어하면 슈퍼클래스의 메소드가 호출될 수 있음
무야호