다형성은 객체지향에서 제일 중요한 부분이라 많이들 말한다. 꼭 이해하고 넘어갈 수 있도록 하자.
다형성
자바에서 다형성은 상위클래스 타입의 참조 변수로 하위 클래스의 인스턴스를 참조할 수 있게 하는 것이다.
아래에 TV 리모컨을 예로 들어놨는데, 다형성은 일종의 컨트롤러(참조 변수)로 사용할 수 있는 멤버의 개수를 조절한다.
여기서 상위클래스가 사용할 수 있는 멤버의 개수는 인스턴스의 멤버 개수보다 같거나 적어야한다. 상속에서 하위 클래스가 상위 클래스보다 같거나 더 많은 멤버 수를 갖는 것과 반대 개념이다. 즉 다형성을 사용하면 상위 클래스의 멤버만 사용 가능하다.
다형성의 핵심 중점은 반복적으로 사용하는 보일러플레이트 코드(반복적으로 사용하는 비슷한 코드)의 최소화를 할 수 있다. 이전에 배웠던 메서드 overriding과 overloading 또한 다형성의 예시라 볼 수 있다.
상위클래스 Friend를 상속받은 BoyFriend, GirlFriend라는 클래스가 있다고 가정하자.
public class practice_1 {
public static void main(String[] args) {
Friend friend = new Friend(); // 객체타입과 참조변수 타입 일치
BoyFriend boyfriend = new BoyFriend(); // 객체타입과 참조변수 타입 일치
Friend girlfriend = new GirlFriend(); // 객체타입과 참조변수 타입 불 일치
// 상위클래스 타입의 참조변수로 하위클래스 객체 참조
GirlFriend girlFriend2 = new Friend(); // ERROR!
//하위클래스 타입의 참조변수로 상위클래스를 참조 불가
//girlFriend2 의 멤버 개수가 상위클래스 Friend 의 멤버 개수보다 많기 때문
TV리모콘을 예시로 든 게 정말 직관적으로 이해가 잘 됐는데 상위클래스 TV가 있고 상속받은 하위 클래스 SmartTV가 있다고 가정해보자. 이들의 멤버는 각자의 TV 리모컨과 SmartTV리모컨으로 다룬다.
상위 클래스의 버튼수는 5개, 하위 클래스는 7개 (상위에서 상속받은 5개 + 하위만의 2개) 버튼이 있는 리모컨을 사용한다. 버튼 수 = 멤버의 개수
이때 하위 클래스의 버튼 수가 더 많으니 상위 클래스의 버튼 수를 전부 커버할 수 있다
(있는 기능을 안 쓰는 건 괜찮다. 2개가 남는 경우) (7개의 멤버이지만 5개는 상속받은 것이므로 사용 가능)
하지만 상위클래스의 리모콘은 버튼이 5개인데 하위클래스는 7개의 버튼이 있다. 상위클래스의 5개의 버튼으로는 나머지 2개에 대한 기능을 수행 못한다. (7개의 멤버 개수를 다뤄야 하는데 다룰 수 있는 멤버가 5개밖에 안되므로 2개의 멤버가 부족하다 -> 없는 걸 호출할 수 없으니 에러)
실제 사용할 수 있는 기능(멤버)의 개수보다 리모콘 버튼수가 많으면 안 된다.
Tv t = new SmartTV(); //OK
SmartTV s = new Tv(); //ERROR!
상위타입 참조변수 -> 하위클래스 가능
하위타입 참조변수 -> 상위 클래스 불가능
참조 변수의 형 변환
참조 변수도 기본형 변수와 같이 형 변환 (casting)이 가능하다
사용할 수 있는 멤버의 개수를 조절하는 것
자료형의 형 변환처럼 값을 변화시키는 게 아니라 사용 가능한 멤버의 개수만 바뀐다 (주소 값, 객체의 변경 X)
참조 변수의 형 변환을 위한 조건은 아래와 같다.
1. 서로 상속관계에 있는 클래스 사이에서만 형 변환이 가능
2. 하위클래스 -----> 상위클래스 (up-casting) 은 형 변환 생략이 가능
3. 상위클래스 -----> 하위클래스 (down-casting)은 형 변환 생략이 불가
**생략 가능/불가 , 업 캐스팅/다운 캐스팅 은 무리해서 외울 필요 전혀 없다.
**상속관계의 클래스 사이에서만 형 변환이 된다는 개념이 제일 중요
상위클래스 Vehicle 을 상속받은 Car클래스와 Supercar클래스 가 있다.
public static void main(String[] args) {
Car car = new Car(); //하위클래스 Car의 참조변수 car 선언
Vehicle vehicle = (Vehicle) car; //상위클래스 Vehicle의 타입으로 참조변수의 형변환 (생략가능)
Car car1 = (Car) vehicle; //하위클래스 Car타입으로 참조변수의 형 변환 (생략 불가)
Supercar spotty = (Supercar) car;
//ERROR!
//Supercar 클래스와 Car클래스는 서로 상속관계에 있지 않으므로 형 변환 자체가 불가
}
상속 관계에 있는 클래스 사이에선 자유롭게 형 변환이 가능하나 하위-> 상위 / 상위-> 하위로 형 변환하는지에 따라서 형 변환 연상자 ( 클래스 )의 생략 유무가 갈린다. 상속 관계에 있지 않은 클래스 사이에서는 형 변환 자체가 불가능하다.
형 변환을 할때 참조 변수들이 가리키는 실제 인스턴스가 뭔지 잘 파악하고, 그 멤버의 개수를 넘어서면 안 된다.
TV 리모콘의 예로 생각해 봤을 때 참조변수의 형 변환 == 리모컨의 변경
즉 사용 가능 멤버의 수를 형 변환을 통해 조절할 수 있다.
Vehicle 멤버수 4개, Car 멤버수 5개일때
Car car = new Car(); //사용가능 멤버수 5개
//하위클래스 Car의 인스턴스를 만들고 참조변수 car에 Car인스턴스 주소값을 저장
Vehicle vehicle = (Vehicle) car; //사용가능 멤버수 4개
//상위클래스 Vehicle의 참조변수 vehicle 에 하위클래스 인스턴스의 참조변수 car를 상위 클래스
//타입으로 형 변환해서 주소값 대입
//여기까지
// car -> Car인스턴스 주소값 (멤버수 5개)
// vehicle -> car가 가지고 있는 인스턴스 주소값 -> 즉 Car의 인스턴스의 주소를 가리킨다.
// 하지만 Vehicle 클래스의 멤버수는 4개이므로 vehicle은 Car의 5개의 인스턴스 멤버중
// 4개만 사용 가능하다.
Car car1 = (Car) vehicle; //사용가능 멤버수 5개
// car1 은 vehicle이 가르키는 주소값을 대입 받으므로 다시 Car 클래스의 인스턴스 주소를 가리킨다.
instanceof 연산자
지금이야 연습하는 코드뿐이라 코드와 클래스의 규모가 작아 형 변환이 가능한지의 여부를 눈으로 직접 보면서 매번 확인하는 것이 가능하나, 나중에 큰 프로젝트를 하거나, 클래스가 많아진다면 매번 직접 찾아 확인하기는 쉽지 않을 것이다. 따라서 가능한 형 변환 전에 instanceof를 통해 확인을 하고 사용하자. 예를 들어 instanceof에서 true가 나오면 if문으로 조건을 걸어서 형 변환을 하도록 하는 장치를 만들 수 있다.
이럴 때 instanceof 연산자를 쓸 수 있는데 boolean타입으로 타입 캐스팅이 가능한지 아닌지의 여부를 확인할 수 있다.
참조_변수 instanceof 검사할_타입
return 값이 true == 검사한 타입으로 형 변환 가능
return 값이 false == 검사한 타입으로 형 변환 불가
아래 연습 예제를 보면 상위클래스 Liquid 가 있고 상속받은 하위클래스 Pepsi와 Water가 있다.
Liquid 타입의 인스턴스만 생성된 상태 -> 따라서 저 상태에서 Water와 Pepsi의 메모리가 없는 상태이므로 확인 불가 -> false 출력
public class instanceof_practice {
public static void main(String[] args) {
Liquid liq = new Liquid();
System.out.println(liq instanceof Pepsi); //false
System.out.println(liq instanceof Water); //false
System.out.println(liq instanceof Object); //true
Liquid pep = new Pepsi();
System.out.println(pep instanceof Object); //true
System.out.println(pep instanceof Liquid); //true
System.out.println(pep instanceof Water); //false (상속관계가 아니므로 형변환 불가)
System.out.println(pep instanceof Pepsi); //true
}
}
'programming > JAVA' 카테고리의 다른 글
[TBC]Java - 객체지향언어 (OOP - Object-oriented Programming) (0) | 2022.05.15 |
---|---|
Java - 추상화 / 인터페이스 (0) | 2022.05.15 |
Java - 캡슐화 (Encapsulation) (0) | 2022.05.12 |
Java - 상속(Inheritance) (0) | 2022.05.12 |
Java - 생성자 (0) | 2022.05.11 |