어제 책의 내용이 계속 이해가 안 돼서 검색하고 gpt랑 싸우고, 책 3회독하고..
어제 푹 자고 일어나서 다시 공부하니 점점.. 이해가 되기 시작한다..
나는 책으로는 100% 이해가 안 돼서, 직접 비유를 만들었는데 이해가 상당히 잘 된다!!!
그래서 노트에 정리한 내용을 약간 더 정리해서 글을 쓸 것이다.
다형성
여러가지 형태를 가질 수 있는 능력
상속과 깊은 관계가 있다.
객체지향을 공부하면 이것도 다형성이고 저것도 다형성이다 하는데
상속에 대해 충분히 공부를 하고 이 글을 읽는 걸 추천한다.
조상 클래스의 참조 변수로 자손 클래스의 인스턴스 참조
조상 클래스의 참조 변수로 자손 클래스의 인스턴스를 참조 가능하다
ex)
List<Integer> list = new ArrayList<>();
이 경우 인스턴스가 ArrayList라도 참조변수 List로는 ArrayList 인스턴스의 모든 멤버를 사용할 수 없다.
List 타입의 참조 변수로는 ArrayList 인스턴스 중에서 List 클래스의 멤버들만 사용할 수 있다.
(List는 사실 인터페이스긴 하지만 일반적인 조상 클래스로 생각해서 봐주길 바란다...)
ArrayList 멤버 중에서 List에 정의되지 않은 멤버는 사용이 불가능하다.
ArrayList<Integer> arrList = new ArrayList<>();
List<Integer> list = new ArrayList<>();
위의 둘다 같은 타입의 인스턴스지만 참조변수의 타입에 따라 사용할 수 있는 멤버의 개수가 달라진다.
그럼 쓰는 이유가 뭘까?
인스턴스 타입과 일치하는 참조변수를 사용하면 인스턴스의 멤버들을 다 사용할 수 있는데? 왜?
gpt한테 물어봤었다.
형변환
참조변수도 형변환이 가능하다
단, 서로 상속관계에 있는 클래스 사이에서만 가능하다
참조변수의 형변환 종류에는 업 캐스팅과 다운 캐스팅이 있다!
업 캐스팅
자손타입의 참조변수를 조상타입의 참조변수로 변환하는 것
다운 캐스팅
조상타입의 참조변수를 자손타입의 참조변수로 변환하는 것
다운 캐스팅의 형변환은 생략이 불가능 하다
캐스트 연산자 '( )' 를 무조건 붙여야 된다는 뜻
왜??????
를 알기 전에 참조 변수... 참조 변수... 계속 이 단어가 나오니까 머리가 더욱 복잡해진다.
아 참조 변수 아는데 왜 헷갈리지????? 이해가 완벽히 안 되니까!!!!
그래서 그 전에 참조 변수에 대한 완벽 이해를 하는 시간을 갖도록 하겠다.
참조 변수 정리
참조변수는 '사용 설명서' 라고 생각하면 이해가 편할 것이다.
만약 TV클래스를 상속 받는 3DTV가 있다고 생각하자
class Tv {
int channel;
}
class ThreeDimensionalTv extends Tv {
Boolean threeDimensional; // 3D 기능을 껐다 키는 상태를 나타낸 변수
}
이제 업 캐스팅을 이용해서 3DTV를 생성 할 것이다.
Tv threeTv = new ThreeDimensionalTv();
Tv 사용 설명서 (참조변수) 에는 채널을 올렸다 내렸다 할 수 있는 channel 사용 설명 밖에 없다.
그래서 3DTV(인스턴스)를 가지고 있는데도 사용 설명서는 TV 사용 설명서니까 3D 기능은 못쓴다.
ThreeDimensionalTv threeTv = new ThreeDimensionalTv();
이렇게 하면 3DTV 사용 설명서랑 3DTV를 갖고 있으니 모든 기능을 사용할 수 있다.
ThreeDimensionalTv threeTv = new Tv(); // 에러 발생
이렇게는 안 된다.
3DTV의 사용 설명서에 적혀있는 기능보다 TV의 기능이 더 적기 때문이다.
3DTV 사용 설명서를 토대로 TV를 다두려고 하면 TV에선 사용할 수 없는 기능이 있기 때문에 생성이 안 된다.
업 캐스팅
위의 예제에서 ThreeDimensionalTv threeTv = new ThreeDimensinalTv(); 를 Tv 참조변수로 형변환을 하면
Tv threeTv1 = threeTv;
이렇게 캐스트 연산자를 생략하고 형변환을 할 수 있는데
이유는 3DTV를 TV 사용 설명서로 사용해도 크게 문제가 되지 않아서 그렇다.
업 캐스팅을 아무리 해도 문제가 없다... 기능만 덜 쓰면 되니까
다운 캐스팅
위의 예제에서 Tv threeTv = new ThreeDimensionalTv(); (TV 사용 설명서와 3DTV를 갖고 있는 상황) 에서 3DTV 사용 설명서를 갖고 싶으면 다운 캐스팅을 하면 된다.
ThreeDimensionalTv threeTv2 = (ThreeDimensionalTv) threeTv;
이 경우 캐스트 연산자를 생략할 수 없다. (= 형변환을 생략할 수 없다)
왜냐하면 업 캐스팅을 하면 문제가 생길일이 전혀 없는데 다운 캐스팅은 문제가 생길 가능성이 있기 때문에 형변환을 생략할 수 없게 해놓은 것이다.
이렇게 설명하니 설명하기가 힘든데 클래스를 더 늘려서 설명하겠다.
class Eletronic {
boolean power; // 전원
}
class Tv extends Eletronic {
int channel; // Tv 채널
public void upChannel () { // 채널 올린 후 채널 출력하는 메소드
channel++;
System.out.println("현재 채널 = " + channel);
}
}
class ThreeDimensionalTv extends Tv {
boolean threeDimensional; // 3D 기능
}
메인 클래스에서 실험해보자
public class Main {
public static void main(String[] args) {
Eletronic tv = new Tv();
tv.upChannel(); // (오류) TV를 가지고 있으나 전자제품 사용설명서엔 upChannel이 없으니 안된다
Tv tv1 = (Tv) tv; // 다운 캐스팅
tv1.upChannel(); // TV를 가지고 있고 tv 사용 설명서에 upChannel을 쓰니 동작한다.
}
}
부모클래스 사용 설명서만 가지고 자손 클래스의 인스턴스를 사용할 수 없다.
public class Main {
public static void main(String[] args) {
Eletronic tv = new Tv();
Tv tv1 = (Tv) tv; // 다운 캐스팅
tv1.upChannel(); // TV를 가지고 있고 tv 사용 설명서에 upChannel을 쓰니 동작한다.
ThreeDimensionalTv tv2 = (ThreeDimensionalTv) tv1;
// TV를 가지고 있고 TV 사용설명서도 있는데 3DTV 사용설명서를 달라고 해본다.
}
}
오류가 발생한다.
왜냐면 tv1은 3DTV 사용 설명서를 쓸 수 없기 때문이다.
근데 사실 컴파일 시에는 오류가 발생하지 않는다. 런타임때 오류가 발생한다.
왜냐면 자바가 그렇게 똑똑하진 않기 때문이다.
자바의 정석에서는 이렇게 말한다.
컴파일 시에는 참조변수간의 타입만 체크하기 때문에 실행 시 생성될 인스턴스의 타입에 대해서는 전혀 알지 못한다.
그래서 instance of 연산자를 쓰는 것이다.
글을 쓰다보니 너무 길어져서 오늘은 여기서 끝!!
'TIL' 카테고리의 다른 글
TIL 2023-11-07 Bean과 IoC 컨테이너 (0) | 2023.11.07 |
---|---|
TIL 2023-11-06 개인프로젝트 완성 및 3 레이어 아키텍처 (0) | 2023.11.06 |
TIL 2023-11-02 Path Variable, Request Param, Model Attribute, Request Body (0) | 2023.11.02 |
TIL 2023-11-01 Spring MVC (0) | 2023.11.01 |
TIL 2023-10-31 Spring과 SpringBoot (0) | 2023.10.31 |