객체지향 프로그래밍의 대표적인 특징으로는
캡슐화, 상속, 다형성이 있어요
그 중에서 다형성은
객체지향 프로그래밍의꽃이라 불립니다.
앞서 학습한 캡슐화나 상속은 직관적으로 이해하기 쉽습니다.
반면에 다형성은 제대로 이해하기도 어렵고
, 잘 활용하기는 더어려워요....
하지만 좋은 개발자가 되기 위해서는 다형성에 대한 이해가 필수!!.
1) 다형성
이름 그대로 "다양한 형태"
의 뜻을 가지고있어요.
즉 , 한 객체가 여러 타입의 객체로
취급될 수 있는 능력을 뜻한다.
다형성을 이해하기 위해서는 크게
2가지 핵심 이론을 알아야 한다.
**다형적 참조**
**메서드 오버라이딩**
1-1)다형적 참조
다형적 참조를 이해하기 위해 다음과 같은
간단한 상속 관계를 코드로 만들어보자.
package poly.basic;
public class Parent {
public void parentMethod() {
System.out.println("##부모메서드호출##");}}
package poly.basic;
public class Child extends Parent {
public void childMethod() {
System.out.println("**자식메서드 호출**");}}
package poly.basic;
public class PolyMain {
public static void main(String[] args) {
/*
* 다형적 참조 :
* 부모는 자식을 품을 수 있다(참조할 수 있다)
*/
//부모변수가 부모인스턴스 참조
System.out.println("Parent -> Parent");
Parent parent = new Parent();
parent.parentMethod();
// Parent.childMethod(); 부모는 자식메서드 사용X
//자식변수가 자식인스턴스 참조
System.out.println("Child -> Child");
Child child = new Child();
child.childMethod();
child.parentMethod();
//부모는 자식을 담을 수 있다.
(Child poly2 = new Parent(); 는 불가능)
자식은 부모를 담을 수 없다
System.out.println("Parent -> Child");
Parent poly = new Child();
poly.parentMethod(); }}
// poly.childMethod();
//자식인스턴스를 참조하지만Parent타입이므로 자식의기능 사용x
Parent parent = new Parent();
자식은 생성되지 않는다
Child child = new Child();
자식인스턴스 생성 시 상속받은
부모까지 같이 생성됨
Parent poly = new Child();
부모는 자식을 담을 수 있다!!!
(자식은 부모를 담을 수 없다)
메모리상에선 child Parent 모두 생성
생성된 참조값을
Parent타입인 poly에 담음
부모방향으로 찾아올라갈순 있어도
자식방향으로 찾아서 내려갈 순 없다!
이 문제를 해결 할 순 없을까???
바로 캐스팅을 이용하면 해결이 가능하기 함!
1-2) 캐스팅
다음 코드를 분석해보자.
부모는 자식을 담을 수 있지만
자식은 부모를 담을 수없다를 기억하자!!!
System.out.println("Parent -> Child");
Parent poly = new Child();
//다운캐스팅
Child child = (Child)poly;
child.childMethod();
child.parentMethod();
`(타입)` 처럼 괄호와 그 사이에 타입을 지정하면
참조 대상을 특정 타입으로 변경할 수 있다.
이렇게 특정 타입으로 변경하는 것을 캐스팅이라 한다.
참고로 캐스팅을 한다고 해서 `Parent poly` 의 타입이 변하는 것은 아니다.
해당 참조값을 꺼내고 꺼낸 참조값이
`Child` 타입이 되는 것이다.
따라서 `poly` 의 타입은 `Parent`
로 기존과 같이 유지됨.
캐스팅의 종류
1) 일시적 다운캐스팅
Parent poly = new Child();
//다운캐스팅
((Child)poly).childMethod();
해당메서드를 호출하는 순간만
다운캐스팅 !
정확히는 `poly` 가 `Child` 타입으로 바뀌는 것은 아니다.
((Child) poly).childMethod() //다운캐스팅을 통해 부모타입을 자식 타입으로 변환 후 기능 호출
((Child) x001).childMethod() //참조값을 읽은 다음 자식 타입으로 다운캐스팅
2)업캐스팅
Child child = new Child();
Parent parent1 = (Parent) child; //업캐스팅은 생략 가능, 생략 권장
Parent parent2 = child; //업캐스팅 생략
parent1.parentMethod();
parent2.parentMethod();
**업캐스팅은 생략할 수 있다.
다운캐스팅은 생략할 수 없다.
참고로 업캐스팅은 매우 자주 사용하기 때문에 생략을 권장!
3)다운캐스팅과 주의점
Parent parent1 = new Child();
Child child1 = (Child) parent1;
child1.childMethod(); //문제 없음
Parent parent2 = new Parent();
Child child2 = (Child) parent2; //런타임 오류 - ClassCastException
child2.childMethod(); //실행 불가
왜 이런 문제가 발생 할까???
다운캐스팅이 가능한 경우
부모가 자식을 품은 경우!
다운캐스팅이 불가능한 경우
부모인스턴스를 참조하고있는데
다운캐스팅 시도 시
(메모리상에 자식 존재X)
이므로 오류발생
업캐스팅이 안전하고 다운캐스팅이 위험한 이유
업캐스팅은 위와 같은 문제가
절대로 일어나지 않음
(인스턴스 생성 시)
해당. 타입의 상위타입이 메모리 상에모두 생성되기 떄문
반면에
다운캐스팅은 메모리에 존재하지않는하위타입으로 변경 하려다가오류가 발생..
`A a = new B()` : A로 업케스팅
`B b = new B()` : 자신과 같은 타입
`C c = new B()` : 하위 타입은 대입할 수 없음, **컴파일 오류**
`C c = (C) new B()` : 하위 타입으로 강제 다운캐스팅, 하지만 B 인스턴스에 C와 관련된 부분이 없으므로 잘
못된 캐스팅, `ClassCastException` **런타임 오류 발생
2) instance of
어떤 인스턴스를 참조하는지
확인하는 방법 !
public class InstanceOf {
public static void main(String[] args) {
Parent parent1 = new Parent(); //Parent 인스턴스 참조하므로 child인스턴스 생성x
System.out.println("** parent1 **");
call(parent1);
Parent parent2 = new Child(); //Child인스턴스 참조 -> 부모인스턴스도 생성됨
System.out.println("** parent2 **");
call(parent2);
}
private static void call(Parent parent) {
parent.parentMethod();
if(parent instanceof Child child) { //오른쪽에 있는 타입이 왼쪽에 들어갈 수 있는가???
System.out.println("child인스턴스 존재");
child.childMethod();}}}
3)다형성과 메서드 오버라이딩
오버라이딩 된 메서드가 항상 우선권을 가진다**는 점
을 기억하자 !!!!
코드로 한번
작성해보자 !!
package poly.basic;
public class OverrideMain {
public static void main(String[] args) {
Child child = new Child();
child.method();
System.out.println("child.value : "+child.value);
System.out.println();
Parent parent = new Parent();
parent.method();
System.out.println("parent.value : "+parent.value);
System.out.println();
Parent poly = new Child();
poly.method();
System.out.println("poly.value : "+poly.value);}}
*자식메서드 호출*
child.value : 자식 멤버변수
##부모메서드호출##
parent.value : 부모의 멤버변수
*자식메서드 호출* ///?????? 분명 Parent타입인데??
poly.value : 부모의 멤버변수
나머지 부분들은 . 다이해가 될것같지만
Parent poly = new Child();
poly.method();
System.out.println("poly.value : "+poly.value);}}
이 부분이 이해가 안 될 것같습니다
먼저 그림으로 보여드리겠습니다!
poly변수는 Parent타입으로
Parent인스턴스를 참조합니다
하지만
Child인스턴스도 메모리상에 존재하며 ,
오버라이딩된 메서드 (method())는
우선권을 가지게됩니다.
필드는 오버라이딩이 되지않으므로
기존 Parent인스턴스의 필드를 참조
`poly.method()` : `Parent` 타입에 있는 `method()` 를 실행하려고 한다.
그런데 하위 타입인`Child.method()` 가 오버라이딩 되어 있다.
**오버라이딩 된 메서드는 항상 우선권**을 가진다. 따라서
`Parent.method()` 가 아니라 `Child.method()` 가 실행된다.
오버라이딩 된 메서드는 항 상 우선권을 가진다
'Java공부(코딩)' 카테고리의 다른 글
코딩초보의 자바(Java)공부 14일차 { 다형성과 설계 } (5) | 2024.12.24 |
---|---|
코딩초보의 자바(Java)공부 13일차 { 다형성 활용 } (0) | 2024.12.23 |
코딩초보의 자바(Java)공부 11일차 { 상속 } (2) | 2024.12.21 |
코딩초보의 자바(Java) 공부 10일차 { 메모리구조와 static } (5) | 2024.12.15 |
코딩초보의 자바(Java)공부 9일차-2 { 접근 제어자 } (0) | 2024.12.14 |