"한 타입의 참조 변수를 통해 여러 타입의 객체를 참조할 수 있도록 하는 것"이다.
즉, 상위 클래스 타입의 참조 변수를 통해서 하위 클래스의 객체를 참조할 수 있도록 허용하여 상위 클래스가 동일한 메시지로 하위 클래스들이 서로 다른 동작을 할 수 있도록 한다.
다시 말해, 같은 코드에서 하나의 메서드나 변수가 다양한 타입의 객체에 대해 동작할 수 있도록 하는 것이다.
여기서 중요한 부분은 우리가 다형성에 대해 배울 때 하나의 자료형이 여러가지 자료형으로 변환될 수 있다고 배운다. (업캐스팅/다운캐스팅)
하지만 엄밀히 따지자면, 메서드가 여러가지 자료형을 인수로 받아서 동작하는 것도 다형성이라고 할 수 있는 것이다. (오버로딩/오버라이딩)
👉 여기서 형변환(Casting)이란?
상속받은 객체에 대한 형변환이 의미하는 것은, 객체에 속한 "멤버들에 대한 사용 범위가 달라진다는 것을 의미"한다.
업캐스팅(upcasting)
(자식 클래스의 인스턴스에 대한) 자식 클래스의 타입의 참조 변수를 부모 클래스 타입으로 형변환 하는 것이다.
자식 => 부모 (up) 변환.
타입 변환 구문 생략 가능. 자동 형변환 됨.
단, 부모 클래스로 캐스팅된다는 것은 멤버 갯수의 감소를 의미한다. 이는 곧 자식 클래스에서만 있는 속성과 메서드는 실행하지 못한다는 뜻이다. (범위 제한)
업캐스팅을 하고 메서드를 실행할 때, 만일 자식 클래스에서 오버라이딩한 메서드가 있을 경우, 부모 클래스의 메서드가 아닌 오버라이딩 된 메서드가 실행되게 된다.
❗ 객체 지향(OOP)의 “참조 다형성”
업캐스팅의 이해는 객체 지향의 참조 다형성에 대해 알고 있다면 크게 어렵지 않다.
쉽게 말하면, 한번에 대입하느냐 변수에 나눠 대입하느냐의 차이가 있을 뿐이다.Parent p1 = new Child(); // 참조 다형성 // --------------------------------------------- Child c1 = new Child(); Parent p1 = c1; // 변수 업캐스팅(upcasting)
다운캐스팅(downcasting)
(자식 클래스의 인스턴스에 대한) 부모 클래스 타입의 참조 변수를 자식 클래스 타입으로 형변환 하는 것이다.
부모 => 자식 (down) 변환.
타입 변환 구문 생략 불가. 형변환 타입을 명시해야됨.
다운캐스팅의 목적은 업캐스팅한 객체를 다시 자식 클래스 타입의 객체로 되돌리는데 목적을 둔다. (범위 복구)
❗ 다운 캐스팅은 부모 클래스를 자식 클래스로 캐스팅하는, 단순히 업캐스팅의 반대 개념이 아니다.
다운 캐스팅의 진정한 의미는 부모 클래스로 업캐스팅된 자식 클래스를 복구하여, 본인의 필드와 기능을 회복하기 위해 있는 것이다.
즉, 원래 있던 기능을 회복하기 위해 다운캐스팅을 하는 것이다.
구분 (사진2) 다운캐스팅 (사진3) 업캐스팅 요약 부모 객체인 p1이 자식 객체로 다운캐스팅 후, 자식 객체인 d1이 부모 객체인 p1으로 업캐스팅됨. 자식 객체인 d1이 부모 객체인 p1으로 업캐스팅 됨. 설명 p1은 자식 객체로 생성되었지만 형태는 부모 객체이므로 사용 범위가 부모(p1)이지만,
d1은 부모인 p1의 자식 객체로 캐스팅되므로 범위 또한 자식(d1)으로 볼 수 있음.d1은 자식 객체이므로 사용 범위가 자식(d1)이지만,
p1은 자식인 d1의 부모 객체로 캐스팅되므로 범위 또한 부모(p1)로 제한됨.
형변환(Casting)의 장점
여러 자식 클래스 타입의 참조 변수들을 하나의 부모 클래스 타입의 참조 변수로 다룰 수 있는 것이다.
만약 아래와 같이, Person이라는 부모 클래스를 가진 자식 클래스 Dancer, Singer, Actor가 있을 경우에 부모 클래스 타입으로 형변환하여 사용하게 되면 아래와 같은 장점이 있다.
여러 자식 클래스 객체를 하나의 배열로 다룰 수 있다.
메서드의 매개변수를 부모 클래스 타입 하나로 전달받아 사용할 수 있다.
활용 예시 코드
// 서로 다른 타입을 각각 정의해서 사용 Dancer[] d = new Dancer[]; d[0] = new Dancer(); d[1] = new Dancer(); Singer[] s = new Singer[]; s[0] = new Singer(); s[1] = new Singer(); Actor[] a = new Actor[]; a[0] = new Actor(); a[1] = new Actor(); //-------------------------------- // 부모 클래스로 묶어서 사용 Person[] p = new Person[]; p[0] = new Dancer[]; p[1] = new Dancer[]; p[2] = new Singer[]; p[3] = new Singer[]; p[4] = new Actor[]; p[5] = new Actor[];위 코드에서 보면 알듯이, 하나의 자료형으로 관리하면 코드량도 훨씬 줄고 가독성 및 유지보수성 또한 좋아진다.
이때, 자식 클래스에만 있는 고유한 메서드를 실행하려면 어떻게 해야 할까?
오버라이딩 한 메서드가 아닌 이상, 업캐스팅한 부모 클래스 타입에서 자식 클래스의 고유 메서드는 실행할 수 없다.
따라서, 업캐스팅한 객체를 다시 자식 클래스 타입으로 되돌리는 다운캐스팅(downcasting)이 필요한 것이다.
public class Parent {
public static void main(String[] agrs) {
Parent p1 = new Parent();
Parent p2 = new Child();
Parent p3 = new ChildOther();
...
위를 보면, 세 개의 객체 모두 클래스 타입은 Parent이다.
하지만 각각 다른 클래스의 생성자를 호출하고 있다.
자바에는 instanceOf라는 메서드를 통해 객체의 클래스 타입을 알 수 있다.
아래 예제를 통해 확인해보자.
instanceOf란?
자바에서 객체의 타입을 확인하는 연산자이다. 형변환 가능 여부를 확인하며, true/false를 반환한다. 주로 상속 관계에서 부모 객체인지 자식 객체인지 확인하는데 사용된다.
쉽게 말해,instanceOf는 "해당 클래스가 자기집이 맞는지 확인해주는 것" 이라고 생각하면 될 것이다.
다형성(polymorphism) 예제 코드
public class Parent {
public static void main(String[] agrs) {
Parent p1 = new Parent();
Parent p2 = new Child();
Parent p3 = new ChildOther();
Parent[] arr = {p1, p2, p3};
for(Parent item : arr) {
System.out.println("---------------");
if(item instanceof Child) {
System.out.println("is Child Type");
}
if(item instanceof ChildOther) {
System.out.println("is ChildOther Type");
}
if(item instanceof Parent) {
System.out.println("is Parent Type");
}
}
}
}
출력
---------------
is Parent Type
---------------
is Child Type
is Parent Type
---------------
is ChildOther Type
is Parent Type
부모 클래스를 제외한 자식 클래스들은 두 가지의 클래스 타입을 가진 것을 볼 수 있다.
이러한 것이 다형성(polymorphism)이다.
같은 클래스 내에서 동일한 이름의 메서드를 매개변수의 개수나 자료형이 다른 여러 개의 메서드로 정의할 수 있는 메서드 정의 기법.
오버로딩은 하나의 클래스 내에서 같은 이름으로 다양한 기느을 수행하는 메서드를 정의하는 기법이다. 여기서 중요한 부분은 매개변수로 다양한 자료형을 받을 수 있으며, 자료형에 따라 메서드의 결과를 다르게 가져올 수 있기도 하다.
오버로딩(overloading) 예시 코드
class OverLoadingTest {
public void test() {
System.out.println("No Parameter");
}
public void test(int count) {
System.out.println("Parameter: " + cont);
}
}
public class OverLoading {
public static void main(String[] args) {
OverLoadingTest olt = new OverLoadingTest();
olt.test();
olt.test(5);
}
}
출력
No Parameter
Parameter: 5
하지만 위와 같이 오버로딩(Overloading)을 사용하기 위해서는 다음의 조건을 만족시켜야 한다.
상속 또는 구현 관계에 있는 상위 객체의 메서드를 하위 객체 또는 구현 객체가 재정의하여 사용하는 메서드 정의 기법
오버라이딩은 상위 객체가 가지고 있는 메서드의 시그니처를 그대로 받아와 재정의하여 사용한다. 이로 인해 하위 클래스는 상위 클래스와 동일한 인터페이스를 제공하며, 상위 클래스와는 다른 기능을 제공할 수 있다.
👉 업캐스팅 오버라이딩 메서드
업캐스팅을 하고 메서드를 실행할 때, 만일 자식 클래스에서 오버라이딩한 메서드가 있을 경우, 부모 클래스의 메서드가 아닌 오버라이딩 된 메서드가 실행되게 된다.
위의 설명을 그냥 보았을 땐 자식 클래스에서 업캐스팅 되었기 때문에 부모 클래스에 정의된 메서드를 사용할 것 같다.
하지만 실제로 보면, 오버라이딩된 자식 클래스의 메서드를 사용하는 것을 볼 수 있다.
-> 이유가 뭘까?
이는 오버라이딩 특성상 코드가 실행하는 "런타임 환경"에서 동적으로 바인딩 되었기 때문이다.class Animal { void sound() { System.out.println("Animal sound"); } } class Dog extends Animal { void sound() { System.out.println("Bark"); } } ... Animal myAnimal = new Dog(); // 업캐스팅 myAnimal.sound(); // 출력 : "Bark"
- 컴파일 시점에서
myAnimal은Animal타입으로,Animal클래스 내에 있는sound()메서드를 호출하는 것으로 인식된다.
- 실행 시점에서는
myAnimal이 실제로 참조하는 객체는Dog클래스의 인스턴스이다.
자바의 동적 바인딩 때문에, 실행 시 실제 객체의 타입을 확인하고 오버라이딩된Dog클래스의sound()메서드가 호출된다.
-> 동적 바인딩의 이유
동적 바인딩은 실행 시점에 객체의 실제 타입을 기준으로 메서드를 결정하기 때문에 프로그램의 유연성을 증가시킨다.
컴파일 시점에서는 해당 변수의 타입만 알 수 있고, 실제 어떤 객체를 참조할지는 실행할 때까지 알 수 없기 때문이다.
업캐스팅 활용 시 주의사항 재정리
- 업캐스팅을 하면 멤버 갯수가 제한 되어 자식 클래스에만 있는 멤버는 사용할 수 없게 된다.
- 업캐스팅 했지만 오버라이딩된 메서드는 자식 클래스의 메서드로 실행이 된다.
오버라이딩(Overriding) 예시 코드
class OverRidingParent {
public void test() {
System.out.println("Parent Method");
}
}
class OverRidingSon extends OverRidingParent {
public void test() {
System.out.println("Child Method Overriding");
}
}
public class Overriding {
public static void main(String[] args) {
OverRidingSon ors = new OverRidingSon();
ors.test();
}
}
출력
Child Method Overriding
오버라이딩도 오버로딩에서 처럼 아래의 조건을 만족시켜야 한다.
오버로딩은 결과적으로 새로운 메서드의 생성이고,
오버라이딩은 부모로부터 상속 받은 메서드를 재정의하는 것이다.
[참고]
https://velog.io/@ung6860/JAVA%EB%8B%A4%ED%98%95%EC%84%B1-%EC%98%A4%EB%B2%84%EB%A1%9C%EB%94%A9-%EC%98%A4%EB%B2%84%EB%9D%BC%EC%9D%B4%EB%94%A9%EC%9D%98-%EC%B0%A8%EC%9D%B4
https://velog.io/@ovan/Overriding-and-Polymorphism
https://ittrue.tistory.com/132
→ 다형성의 장점 및 조건
https://kadosholy.tistory.com/99
→ 형변환 이해 & 예제 (+사진)
https://inpa.tistory.com/entry/JAVA-%E2%98%95-%EC%97%85%EC%BA%90%EC%8A%A4%ED%8C%85-%EB%8B%A4%EC%9A%B4%EC%BA%90%EC%8A%A4%ED%8C%85-%ED%95%9C%EB%B0%A9-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0
→ 형변환 업캐스팅/다운캐스팅 개념 이해 & 예제