다형성

이용만·2023년 2월 27일
0

Jo쌤이 말씀하신 중요사항

  • 캡상추다는 연결되어 있다.
  • 상속은 캡슐화를 위반하는 경우가 있다. 상속 자체가 강한 결합이 된다. 자주 사용되는 패턴은 아니다.
  • 동일한 타입의 참조변수에서 다양한 결과가 나오는 것이 핵심이다.
  • 전제조건은 상위 클래스 타입 변수에 하위 클래스 인스턴스를 참조할 수 있어야 된다.

👉 다형성

자바 프로그래밍에서의 다형성은 한 타입의 참조변수를 통해 여러 타입의 객체를 참조할 수 있도록 만든 것
구체적으로 상위 클래스 타입의 참조변수를 통해서 하위 클래스의 객체를 참조할 수 있도록 허용한 것이다.

public class Friend {
    public void friendInfo(){
        System.out.println("나는 당신의 친구입니다.");
    }
}
public class BoyFriend extends Friend{
    public void friendInfo(){
        System.out.println("나는 당신의 남자친구입니다.");
    }
}
public class GirlFriend extends Friend{
    public void friendInfo(){
        System.out.println("나는 당신의 여자친구입니다.");
    }
}
public static void main(String[] args) {
        Friend friend = new Friend(); // 객체 타입과 참조변수 타입의 일치
        BoyFriend boyFriend = new BoyFriend();
        Friend girlFriend = new GirlFriend(); // 객체 타입과 참조변수 타입 불일치

        friend.friendInfo(); // 나는 당신의 친구입니다.
        boyFriend.friendInfo(); // 나는 당신의 남자친구입니다.
        girlFriend.friendInfo(); // 나는 당신의 여자친구입니다.
    }
// 출력값
나는 당신의 친구입니다.
나는 당신의 남자친구입니다.
나는 당신의 여자친구입니다.

Friend girlFriend = new GirlFriend();
GirlFriend 클래스의 인스턴스를 생성하고 그것을 Friend 타입의 참조변수 girlFriend에 할당하고 있다.
이 경우, 상위 클래스를 참조변수의 타입으로 지정했기 때문에 자연스럽게 참조변수가 사용할 수 있는 멤버의 개수는 상위 클래스의 멤버의 수가 된다.
이것이 다형성의 핵심적인 부분인 '상위 클래스 타입의 참조변수로 하위 클래스의 객체를 참조하는 것
또한 하위 클래스 타입으로 상위 클래스 객체 참조는 불가능하다.


❓이해가 안되서 생활 코딩에서 본 다른 예시

public class A {
    public String x(){return "x";}
}
public class B extends A{
    public String y(){ return "y";}
}
public class HelloJava {
    public static void main(String[] args) {
        A obj = new B();
        obj.x();
        obj.y(); // 에러 발생
    }
}

클래스 B를 인스턴스화한 obj 변수가 현재 A 클래스 행세를 한다.
x메소드를 실행하면 x메소드의 소재는 클래스 A이다.
obj참조변수는 클래스 B를 인스턴스화 하지만 클래스 A 행사를 하고 있다.
클래스 A는 메소드 x가 있기에 정상적으로 실행이 되지만
y메소드가 실행 안되는 이유는 obj 참조변수는 클래스 A의 행사를 하고 있기 때문이다.

두가지를 기억해야 한다.
1. 어떤 클래스를 인스턴스화 시킬 때 인스턴스를 담는 참조변수의 데이터 타입은 상위클래스가 될수도 있다.

public class A {
    public String x(){
        return "A.x";
    }
}
public class B extends A{
    public String x(){ return "B.x";}
    public String y(){
        return "y";
    }
}
public static void main(String[] args) {
        A obj = new B();
        obj.x();
    }
출력결과 : B.x

클래스 B 를 인스턴스화한 obj 참조변수는 클래스 A 행세를 하고 있지만
실행한 x 메소드는 B에 정의되어 있는 x 메소드를 실행된다.
obj 인스턴스는 A 클래스 행세를 하고 있기에 클래스 A에 정의되지 않은 y를 실행하면 에러가 발생한다.
그래서, 클래스 B를 인스턴스화한 obj 객체가 A 클래스 타입이기에 A의 메소드를 실행하지만 클래스 B에 있는 메소드가 상위 A 클래스 메소드를 오버라이딩 했다면 인스턴스화 시킨 B 클래스의 메소드를 실행시키게 되는 것이다.(😯 이걸 대체 왜 쓰는거죠?🧠 🧠 )

👉참조 변수의 타입 변환

기본 자료형의 형변환도 가능한 것처럼 참조 변수도 타입 변환이 가능하다.

참조 변수 타입 변환을 위한 세가지 조건
1. 서로 상속관계에 있는 상위 클래스 - 하위 클래스 사이에만 타입 변환이 가능
2. 하위 클래스 타입에서 상위 클래스 타입으로의 타입 변환(업캐스팅)은 형변환 연산자를 생략할 수 있다.
3. 반대로 상위 클래스에서 하위 클래스 타입으로 변환(다운캐스팅)은 형변환 연산자를 반드시 명시해야 된다.

public class HelloJava {
    public static void main(String[] args) {
        Car car = new Car();
        Vehicle vehicle = (Vehicle) car;// 상위 클래스 Vehicle 타입으로 변환(생략 가능)
        Car car1 = (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("묘기 부리기");
    }
}

👉 instanceof 연산자

instanceof 연산자는 참조변수의 타입 변환, 즉 캐스팅이 가능한지 여부를 boolean 타입으로 확인합니다.

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{};

👉다형성 활용예제

public class PolymorphismEx {
  public static void main(String[] args) {
    Customer customer = new Customer();
    customer.buyCoffee(new Americano());
    customer.buyCoffee(new CaffeLatte());

    System.out.println("현재 잔액은 " + customer.money + "원 입니다.");
  }
}

class Coffee {
  int price;

  public Coffee(int price) {
    this.price = price;
  }
}

class Americano extends Coffee {
  public Americano() {
    super(4000); // 상위 클래스 Coffee의 생성자를 호출
  }

  public String toString() {return "아메리카노";}; //Object클래스 toString()메서드 오버라이딩
};

class CaffeLatte extends Coffee {
  public CaffeLatte() {
    super(5000);
  }

  public String toString() {return "카페라떼";};
};

class Customer {
  int money = 50000;

  void buyCoffee(Coffee coffee) {
    if (money < coffee.price) { // 물건 가격보다 돈이 없는 경우
      System.out.println("잔액이 부족합니다.");
      return;
    }
    money = money - coffee.price; // 가진 돈 - 커피 가격
    System.out.println(coffee + "를 구입했습니다.");
  }
}

// 출력값
아메리카노를 구입했습니다.
카페라떼를 구입했습니다.
현재 잔액은 41000원 입니다.

buyCoffee() 메소드의 매개변수를 Coffee 타입으로 전달해주었다.
객체를 생성하고 참조변수를 사용할 때 Coffee 클래스를 상속받기만 하면 buyCoffee(Coffee coffee) 메소드의 매개변수로 전달할 수 있다.
이와같이 다형성을 잘 활용하면 많은 중복되는 코드를 줄이고 편리하게 코드를 작성할 수 있게 된다.

profile
성장하는 개발자가 되고자 합니다.

0개의 댓글