다형성
일반적인 의미에서 다형성이란 하나의 객체가 여러가지 형태를 가질 수 있는 것을 말한다.
자바에서 다형성은 무엇일까?
자바에서 다형성은 한 타입의 참조변수를통해 여러 타입의 객체를 참조 할 수 있도록 만든 것 이라고 한다.
상위 클래스 타입의 참조변수를 통해 하위클래스의 객체를 참조할 수 있도록 허용한 것이라고 하는데
이게 뭔말인지 감이 잘 안잡힌다.
코드를 읽어보면서 이해하는것이 나을 것 같다.
코드 예시
public class FriendTset {
public static void main(String[] args) {
Friend friend = new Friend();
BoyFriend boyFriend = new BoyFriend();
Friend girlfrend = new GirlFriend();
friend.friendInfo();
boyFriend.friendInfo();
girlfrend.friendInfo();
}
}
class Friend{
public void friendInfo(){
System.out.println("im youer friend");
}
}
class BoyFriend extends Friend{
public void friendInfo(){
System.out.println("im youer boy");
}
}
class GirlFriend extends Friend{
public void friendInfo(){
System.out.println("im youer girl");
}
}
//출력
im youer friend
im youer boy
im youer girl
위의 코드를 보면, 생위클래스를 Friend로 두고, 거기에 BoyFriend, GirlFriend 클래스를 만들어서 상속 시켯다.
위에서 객체를 선언할 때 유심히 보면
Friend friend = new Friend();
이 부분은 Friend 타입과 일치하게 참조변수 타입을 사용하였다.
BoyFriend boyFriend = new BoyFriend();
이 부분도 BoyFriend 타입과 일치하게 참조변수 타입을 사용하였다.
Friend girlfrend = new GirlFriend();
이 부분은 Friend 타입의 참조변수를 girlfrend 에 생성된 GirlFriend 인스턴스를 할당하였다.
이 때, 상위클래스인 Friend 를 참조하여 선언하였기 때문에 자연스럽게 사용 할수 있는 맴버의 갯수는 상위클래스의 맴버 수 만큼이다.
반대로 하위클래스로 상위클래스를 할당해서 하면 안되냐? 할 수 있는데
안된다.
왜냐면, 하위클래스의 맴버 수는 상위클래스에 비해 같거나, 더 많은데, 이 경우 상위클래스에 없는 변수들을 설정할 수가 없기 때문이다.
참조변수의 타입 변환
자료형의 형변환 처럼, 참조변수도 타입변환이 가능하다.
다만, 자료형은 데이터 형식을 변환하는 거지만(int를 String으로 변환한다던가..), 참조변수의 타입변화는 좀 다르다.
참조변수 타입변화는 사용할 맴버의 개수를 조절하는 것이다.
위에서 다형성을 예로 들 때 참조변수로 다른 객체의 맴버들을 불러 왓는데, 참조변수는 이 맴버들 중에 사용하고 싶은 애들만 골라서 할 수 있게 해주는 것이라고 생각하면 쉽다.
참조변수 타입변화를 위해서는 3가지 조건이 필요하다.
- 서로 상속관계에 있는 상위 클래스 - 하위클래스 사이에만 타입 변환이 가능
- 하위 클래스 타입에서 상위 클래스 타입으로의 타입변환 (하위 -> 상위로 변환 하는것은 업케스팅이라고 한다.)은 형변환 연산자 (괄호)를 생략할 수 있다. (마치 double형을 int로 바꿀 때 (int) double 로 하는것 처럼 괄호를 붙인다.)
- 상위 클래스에서 하위 클래스 타입으로 변환 (상위 -> 하위로 변환하는 것을 다운캐스팅이라고 한다.)은 형변환 연산자(괄호)를 반드시 명시해야 한다.
코드 예시
public class VehicleTest {
public static void main(String[] args) {
Car car = new Car();
Vehicle vehicle = (Vehicle) car; // 상위 클래스 Vehicle 타입으로 변환(생략 가능)
Car car2 = (Car) vehicle; // 하위 클래스 Car타입으로 변환(생략 불가능)
MotorBike motorBike = (MotorBike) car; // 상속관계가 아니므로 타입 변환 불가 -> 에러발생
}
}
class Vehicle {
String model;
String color;
int wheels;
void startEngine() {
System.out.println("시동 걸기");
}
void accelerate() {
System.out.println("속도 올리기");
}
void brake() {
System.out.println("브레이크!");
}
}
class Car extends Vehicle {
void giveRide() {
System.out.println("다른 사람 태우기");
}
}
class MotorBike extends Vehicle {
void performance() {
System.out.println("묘기 부리기");
}
}
위의 코드를 보면, Vehicle 클래스를 Car, MotorBike 클래스가 상속 받은 형태를 볼 수 있다.(출력은 신경쓰지 말자)
Car 참조변수로 선언된 car는 상위 클래스(Vehicle / 업캐스팅) 로 형변환이 가능하고
Car 참조변수로 car2를 선언할 때 상위클래스(Vehicle) 를 하위클래스(Car)로 형변환(다운캐스팅)이 가능하다.
하지만 Car, MotorBike 클래스는 모두 Vehicle을 상속 받은 클래스이지만 서로 상속관계가아니기 때문에 형변환이 불가능 하다.
instanceof 연산자
instanceof 연산자는 위에서 설명한 참조변수 타입 변환(캐스팅)이 되는지 알려주는 연산자이다.
boolean타입으로 캐스팅이 가능한지 아닌지 알려준다.
위에서 캐스팅 할때 조건 중에 "객체를 어떤 생성자로 만들었는지" 랑 "클래스 사이에 상속관계가 존재하는지"를 판단해야 한다.
이걸 일일히 머릿속으로 기억하고 코드를 짜거나 일단 해보고 오류나는지 아닌지 보려면 엄청나게 번거로울 것이다.
instanceof 연산자를 사용함으로 이를 해결 할 수 있다.
작성예시
참조_변수 instanceof 타입
위의 코드 양식에 맞추어 나온결과값이 true면 캐스팅 가능, false가 나오면 캐스팅 불가이다.
코드 예시
public class InstanceOfExample {
public static void main(String[] args) {
Animal animal = new Animal();
System.out.println(animal instanceof Object); //true
System.out.println(animal instanceof Animal); //true
System.out.println(animal instanceof Bat); //false
Animal cat = new Cat();
System.out.println(cat instanceof Object); //true
System.out.println(cat instanceof Animal); //true
System.out.println(cat instanceof Cat); //true
System.out.println(cat instanceof Bat); //false
}
}
class Animal {};
class Bat extends Animal{};
class Cat extends Animal{};
위 코드에서 주의깊게 봐야 하는 것은 상속 관계이다.
Bat 는 Animal에 상속 되어 있다. -> Animal = 상위 클래스 , Bat = 하위클래스
Cat 는 Animal에 상속 되어 있다. -> Animal = 상위 클래스 , Cat = 하위클래스
첫 번째 출력에서 Object는 Anima의 객체인 animal의 상위 클래스이므로 캐스팅이 가능하다 -> true 반환
두 번째 출력에서 Animal은 animal의 클래스이므로 캐스팅이 가능하다. (자기자신) -> true 반환
세 번째 출력에서 Bat 클래스는 Animal의 상속 클래스이나, animal이 들어 갈 수 없다 ->false 반환
세번째 출력에 약간 의구심을 품을 수 있는데, 다른 블로그를 보니 적절한 비유가 있어 이를 인용하겠다.
instanceof 연산자는 자기 집이 맞는지 , 들어갈 수 잇는것인지 확인하는 것이라고 생각하자.
상속을 받은 자식(하위클래스의 객체)은 자기집(하위클래스), 부모님집(상위클래스)에 들락거릴 수 있다. (첫 번째, 두 번째 출력)
그런데 부모님(상위클래스의 객체)이 자식 집(하위클래스)에 오는건 쵸큼...(신혼이라고 치면 더더욱) / (세 번째 출력)
즉 instanceof 를 사용해서 true가 나오게 하고 싶으면
하위클래스에서 나온 객체 instanceof 상위 클래스
위와 같이 쓰면된다.
다형성 활용
public class PolymorphismEx {
public static void main(String[] args) {
Customer customer = new Customer();
customer.buyCoffee(new Americano());
customer.buyCoffee(new CaffeLatte());
System.out.println("rest money is : " + customer.money);
}
}
class Coffee{
int price;
public Coffee(int price){
this.price = price;
}
}
class Americano extends Coffee {
public Americano(){
super(4000);
}
public String toString(){
return "Americano";
}
}
class CaffeLatte extends Coffee {
public CaffeLatte(){
super(5000);
}
public String toString(){
return "CaffeLatte";
}
}
class Customer{
int money =50000 ;
void buyCoffee(Coffee coffee){
if(money < coffee.price){
System.out.println("low money");
return;
}
money = money - coffee.price;
System.out.println(coffee + "buy");
}
}
손님의 주문 메뉴, 가격을 입력 받아서 잔액을 출력하는 예제
Coffee를 상속 받은
-> Americano, CaffeLatte 가 Coffee안의 생성자 에 가격을 넣고, 커피명을 반환한다
-> 2개의 변수 반환
-> Customer 클래스에서는 이 반환 받은 변수 buyCoffee메서드 (Coffee coffee로 받아서 coffee.price 로 가격 받고, coffee에는 커피 이름이 들어 있음)를 활용해서 잔액에 따라 구입, 구입불가 출력
핵심은, buyCoffee메서드에 매개변수로 Coffee타입을 전달하고, 이후 객체를 생성, 참조변수를 사용할 때 Coffee의 클래스를 상속 받은 상태이기만 하면 buyCoffe(Coffee coffee)의 매개변수로 전달 가능하다.
만약 위와같이 작성하지 않았다면, Americano, CaffeLatte클래스에 각각 ~를 구입했습니다 + 잔액을 구하는 코드 를 일일히 써야 해서 총 두번 써야 했을 것이다.
만약 2개가 아니고 수백 수만개면 이거 못한다. 그래서 다형성으로 이를 해결한 것이다.
'백엔드 > JAVA_이론공부' 카테고리의 다른 글
JAVA_ final 키워드 (0) | 2022.09.22 |
---|---|
JAVA_추상화 , 추상 클래스, abstract 제어자 (0) | 2022.09.22 |
JAVA_접근제어자 , getter, setter (0) | 2022.09.18 |
JAVA_캡슐화, 패키지, import (0) | 2022.09.18 |
JAVA_super, super() (0) | 2022.09.18 |