Ch7. 상속 -2

JinJinJara·2024년 4월 24일
0

이것이 자바다

목록 보기
1/1

7. 타입 변환과 다형성

1) 다형성

동일한 타입을 사용하지만 실행 결과가 다양한 결과(객체)가 나오며 이용할 수 있는 성질

서로 다른 객체들이 연결되고 자신의 역할을 수행할 때, 다른 객체로 교체될 수 있어야 한다.
이를 위해 다형성이 필요하다.
다형성을 프로그램상에서 구현하기 위해서는 상속, 오버라이딩, 타입 변환을 이용한다.

2) 자동 타입 변환 (Promotion)

: 프로그램 실행 도중 컴파일러에 의해 자동으로 타입 변환이 일어나는 것

부모클래스 변수 = 자식 클래스 타입;

  • 부모의 특징과 기능을 상속 받기 때문에 부모와 동일하게 취급될 수 있다.
public class Animal {
   boolean isMale;
   public void makeSound(){
      System.out.println("kkkkk");
   }
}

public class Cat extends Animal {
   String name;
   public Cat(String name){
      super();
      this.name = name;
   }
   @Override
   public void makeSound(){
      System.out.println("meowww");
   }
   
   public String getName(){
      return this.name;
   }
}

public class Example{
   public static void main(String[] args){
   
      Animal animal = new Cat("kitty");
      animal.makeSound(); // meowww
      animal.getName(); // COMPILE ERROR!
   }
}
  • 코드 설명

    • Heap 영역에는 Animal 클래스의 특성을 상속받은 Cat 객체가 생성된다.

    • 해당 객체는 Animal 타입으로 참조되는 클래스 타입이다.

    • Animal 타입은 animal 변수가 참조할 수 있는 객체의 종류로, 메소드 영역에 존재하는 Animal 클래스의 구조와 메소드를 참조할 수 있게 된다.

    • animal 변수로 메소드를 호출할 때는 Cat 객체의 Overriding 된 메소드를 호출한다.

    • Cat 객체가 Animal 클래스로 업캐스팅 되었을 때, Cat 클래스에만 있던 인스턴스 멤버는 참조 변수를 통해 직접 참조할 수 없다.

    • Animal 타입의 참조 변수로는 Cat 클래스에 정의된 getName() 메소드에 접근할 수 없기 때문에 컴파일 에러가 발생한다.

    • Cat 타입으로 명시적으로 캐스팅한 후에 getName() 메소드를 호출해야 한다.

3) 필드의 다형성

: 필드의 타입에는 변화가 없지만, 실행 도중 어떤 객체를 필드로 저장하느냐에 따라 실행 결과가 달라질 수 있다.

public class Bicycle {
   Tire frontTire = new Tire("앞바퀴", 7);
   Tire backTire = new Tire("뒷바퀴", 5);
   
   int run(){
      System.out.println("자전거 출발!");
      if (!frontTire.roll()){ stop(); return 1; }
      if (!backTire.roll()){ stop(); return 2; }
      return 0;
   }
   void stop(){ System.out.println("자전거 멈춤!"); }
}

public class Tire {
   public String location;
   public int maxRotation;
   public int accumulatedRotation;
   
   public Tire(String location, int maxRotation){
      this.location = location;
      this.maxRotation = maxRotation;
   }
   
   public boolean roll(){
      accumulatedRotation++;
      if(accumulatedRotation < maxRotation){
         System.out.println(location + "타이어 수명 : "
         					+ (maxRotation-accumulatedRotation));
         return true;
      }else{
         System.out.println(location + "타이어 펑크");
         return false;
      }
   }
}

class HankookTire extends Tire {
   public HankookTire(String location, int maxRotation){
      super(location, maxRotation);
   }
}

class KumhoTire extends Tire {
   public KumhoTire(String location, int maxRotation){
      super(location, maxRotation);
   }
}

public class Example {
   public static void main(String[] args){
      Bicycle bicycle = new Bicycle();
      int tireLocation = 0;
      
      for(int i=1; i<=3; i++){
         tireLocation = bicycle.run();
         if (tireLocation == 0) break; // 타이어 문제 없으면 반복 종료
         switch(tireLocation){
            case 1:
               System.out.println("앞바퀴 교체!");
               bicycle.frontTire = new HankookTire("앞바퀴", 14);
               break;
            case 2:
               System.out.println("뒤바퀴 교체!");
               bicycle.backTire = new KumhoTire("뒤바퀴", 10);
               break;
         }
      }
   }
}

3) 하나의 배열로 객체 관리

  • 동일한 타입의 객체 필드를 배열로 관리
public class Bicycle {
   Tire[] tires = {
      Tire frontTire = new Tire("앞바퀴", 7);
      Tire backTire = new Tire("뒷바퀴", 5);
   }
   int run(){
      System.out.println("자전거 출발!");
      for(int i; i<tires.length; i++){
         if(tires[i].roll()==false){
            stop();
            return (i+1);
         }
      }
      return 0;
   }
   void stop(){ System.out.println("자전거 멈춤!"); }
}

public class Example {
   public static void main(String[] args){
      Bicycle bicycle = new Bicycle();
      
      for(int i=1; i<=5; i++){
         int problemTire = bicycle.run();
         if(problemTire != 0){
            System.out.println(bicycle.tires[problemTire-1].location+ "교체");
            
            bicycle.tires[problemTire-1]
            	= new HankookTire(bicycle.tires[problemTire-1].location, 15);
         }
      }
   }
}

4) 매개 변수의 다형성

  • 자동 타입 변환 : 필드의 값을 대입할 때, 메소드 호출할 때 많이 발생한다.
  • 강제 타입 변환 : 부모 타입을 자식타입으로 변환하는 것
    • 자식클래스 변수 = (자식클래스) 부모클래스 타입
public class Bicycle{
   void start(Tire tire){
      tire.roll();
   }
}

public class AbcTire extends Tire {
   public int price;
   
   @Override
   public void roll(){
      System.out.println("AbcTire 데굴");
   }

}

public class Example{
   public static void main(String[] args){
   
      // 필드 자동 타입변환
      Tire tire = new AbcTire();
      tire.roll();			// AbcTire 데굴
      
      // 강제 타입 변환
      AbcTire abcTire = (AbcTire) tire;  
      abcTire.price = 100000;
      
      // 메소드 자동 타입변환
      Bicycle bicycle = new Bicycle();
      AbcTire abc = new AbcTire();
      bicycle.start(abc); // Tire -> AbcTire 자동 타입 변환!
   }
}

5) 객체 타입 확인 (instanceof)

Parent parent = new Child();
Child child = (Child)parent;
  • parent(부모 객체 타입 변수)가 자식 객체를 참조하고 있기 때문에,
    parent 는 자식 객체 타입으로 다운 캐스팅이 가능하다.
Parent parent = new Parent();
Child child = (Child) parent;
  • parent(부모 객체 타입 변수)는 부모 객체만를 참조하고 있기 때문에,
    다운 캐스팅 할 경우 ClassCastException 이 발생한다.
    = parent 을 다운 캐스팅할 수 없다.

instanceof 연산자

  • 부모 변수가 참조하는 객체가 Parent 인지 Child 인지 확인하는 방법
  • boolean res = 객채 instanceof 타입
if(parent instanceof Child){
   Child child = (Child)parent;
   System.out.println("강제 타입 변환");
}

8. 추상 클래스

  • 실체간의 공통되는 필드와 메소드를 추출해 선언한 클래스

  • 객체를 직접 생성해서 사용할 수 없고, 상속을 통해 자식 클래스만 생성 가능하다.

1) 추상 클래스의 용도

  1. 실체 클래스의 공통된 필드와 메소드의 이름을 통일할 목적
  2. 실체 클래스를 작성할 때 시간 절약

2) 추상 메소드와 오버라이딩

  • 추상 메소드 : 추상 클래스 메소드가 반드시 작성되어야 할 부분일 때 사용
public abstract class Car{
   public String brand;
   public Car(String brand){
      this.brand = brand;
   }
   public abstract void start();
   
   public void driving(){
      System.out.println("운전중...");
   }
   
}

public class ElectronicCar extends Car{
   public ElectronicCar(String brand){
      super(brand);
   }
   
   @Override
   public void start(){
      System.out.println("시동 버튼 누르기");
   }

   public void charging(){
      System.out.println("충전중...");
   }
}

public class Example{
   public static void main(String[] args){
      ElectronicCar elCar = new ElectronicCar("테슬라");
      
      carStart(elCar);  // 자동 타입 변환
      elCar.driving();  // 운전중...
      elCar.charging(); // 충전중...
   }
   
   public void carStart(Car car){
      car.start();		// 시동 버튼 누르기
   }
}

0개의 댓글