Language/JAVA

[JAVA] 상속2

IT수정 2024. 9. 4. 17:03

final 클래스와 final 메서드

필드 선언 시에 final을 붙이면 초기값 설정 후 값을 변경할 수 없다. 그렇다면 클래스와 메서드에 final을 붙이면 어떤 효과가 일어날까? final 클래스와 final 메서드는 상속과 관련이 있다.

 

final 클래스

클래스가 선언할 때 final 키워드를 class 앞에 붙이면 최종적인 클래스이므로 더 이상 상속할 수 없는 클래스가 된다. 즉 final 클래스는 부모 클래스가 될 수 없어 자식 클래스를 만들 수 없다.

public final class 클래스 {...}

 

final 메서드

메서드를 선언할 때 final 키워드를 붙이면 이 메서드는 최종적인 메서드이므로 오버라이딩할 수 없는 메서드가 된다. 즉 부모 클래스를 상속해서 자식 클래스를 선언할 때, 부모 클래스에 선언된 final 메서드는 자식 클래스에서 재정의할 수 없다.

public final 리턴타입 메서드(매개변수, ...) {...}

 

protected 접근 제한자

protected 접근 제한자는 상속과 관련이 있고, public과 default의 중간쯤에 해당하는 접근 제한을 한다.

접근 제한자 제한 대상 제한 범위
protected 필드, 생성자, 메서드 같은 패키지 이거나, 자식 객체

 

protected는 같은 패키지에서는 default처럼 접근이 가능하나, 다른 패키지에서는 자식 클래스만 접근을 허용한다.

 

타입 변환

클래스의 타입 변환은 상속 관계에 있는 클래스 사이에서 발생한다.

 

자동 타입 변환

자식은 부모의 특징과 기능을 상속받기 때문에 부모와 동일하게 취급될 수 있다. 예를 들어 고양이가 동물의 특징과 기능을 상속받았다면 '고양이는 동물이다'가 성립한다.

Cat cat = new Cat();
Animal animal = cat;

// Animal animal = new Cat(); 도 가능

 

위 코드의 cat과 animal의 변수는 타입만 다를 뿐, 동일한 Cat 객체를 참조한다. 따라서 두 참조 변수의 == 연산 결과는 true가 나온다.

 

바로 위의 부모가 아니더라도 상속 계층에서 상위 타입이라면 자동 타입 변환이 일어날 수 있다.

 

부모 타입으로 자동 타입 변환된 이후에는 부모 클래스에 선언된 필드와 메서드만 접근 가능하다. 비록 변수는 자식 객체를 참조하지만 변수로 접근 가능한 멤버는 부모 클래스로 한정된다. 그러나 자식 클래스에서 오버라이딩된 메서드가 있다면 부모 메서드 대신 오버라이딩된 자식 메서드가 호출된다. 이것은 다형성과 관련이 있다.

 

강제 타입 변환

자식 타입은 부모 타입으로 자동 변환되지만, 반대로 부모 타입은 자식 타입으로 자동 변환 되지 않는다. 대신 캐스팅 연산자로 강제 타입 변환을 할 수 있다. 그렇다고 해서 부모 타입 객체를 자식 타입으로 무조건 강제 변환할 수 있는 것은 아니다. 자식 객체가 부모 타입으로 자동 변환된 후 다시 자식 타입으로 변환될 때 강제 타입 변환을 사용할 수 있다.

Parent parent = new Child(); //자동 타입 변환
Child child = (Child) parent; //강제 타입 변환

 

자식 객체가 부모 타입으로 자동 변환하면 부모 타입에 선언된 필드와 메서드만 사용 가능하다는 제약 사항이 따른다. 만약 자식 타입에 선언된 필드와 메서드를 꼭 사용해야 한다면 강제 타입 변환을 해서 다시 자식 타입으로 변환해야 한다.

 

다형성

다형성이란 사용 방법은 동일하지만 실행 결과가 다양하게 나오는 성질을 말한다. 자동차의 부품을 교환하면 성능이 다르게 나오듯이 객체는 부품과 같아서, 프로그램을 구성하는 객체를 바꾸면 프로그램의 실행 성능이 다르게 나올 수 있다.

 

객체 사용 방법이 동일 하다는 것은 동일 메서드를 가지고 있다는 뜻이다. 다형성을 구현하기 위해서는 자동 타입 변환과 메서드 재정의가 필요하다.

 

필드 다형성

필드 다형성은 필드 타입은 동일하지만(사용 방법은 동일하지만), 대입되는 객체가 달라져서 실행 결과가 다양하게 나올 수 있는 것을 말한다.

public class Tire {
	public void roll() {
    System.out.println("회전합니다.");
    }
  }
public class HankookTire extends Tire {
	@Override
    public void roll() {
    System.out.println("한국 타이어가 회전합니다.");
    }
  }
public class KumhoTire extends Tire {
	@Override
    public void roll() {
    System.out.println("금호 타이어가 회전합니다.");
    }
  }
public class Car {
	public Tire tire;
    
    public void run() {
	tire.roll();
    }
  }
public class CarExample {
	public static void main(String[] args) {
	Car myCar = new Car();
    
    myCar.tire = new Tire();
    myCar.run();
    
    myCar.tire = new HankookTire();
    myCar.run();
    
    myCar.tire = new KumhoTire();
    myCar.run();
    }
 }

 

실행 결과

회전합니다.
한국 타이어가 회전합니다.
금호 타이어가 회전합니다.

 

매개변수 다형성

다형성은 필드보다는 메서드를 호출할 때 많이 발생한다. 메서드가 클래스 타입의 매개변수를 가지고 있을 경우, 호출할 때 동일한 타입의 객체를 제공하는 것이 정석이지만 자식 객체를 제공할 수도 있다. 여기서 다형성이 발생한다.

public class Vehicle {
	public void run() {
    System.out.println("차량이 달립니다.");
    }
  }
public class Bus extends Vehicle {
	@Override
    public void run() {
    System.out.println("버스가 달립니다.");
    }
  }
public class Taxi extends Vehicle {
	@Override
    public void run() {
    System.out.println("택시가 달립니다.");
    }
  }
public class Driver {
	public void dirve(Vehicle vehicle) {
    	vehicle.run();
        }
     }
public class DriverExample {
	public static void main(String[] args) {
    	Driver driver = new Driver();
        
        Bus bus = new Bus();
        driver.drive(bus);
        
        Taxi taxi = new Taxi();
        driver.drive(taxi);
        }
     }

 

실행 결과

버스가 달립니다.
택시가 달립니다.

 

객체 타입 확인

매개변수의 다형성에서 실제로 어떤 객체가 매개값으로 제공되었는지 확인하는 방법이 있다. 꼭 매개변수가 아니더라도 변수가 참조하는 객체의 타입을 확인하고자 할 때, instanceof 연산자를 사용할 수 있다. instanceof 연산자의 좌항에는 객체가 오고 우항에는 타입이 오는데, 좌항의 객체가 우항의 타입이면 true를 산출하고 그렇지 않으면 false를 산출한다.

boolean result = 객체 instanceof 타입;

 

다음 코드는 Child 타입으로 강제 타입 변환하기 전에 매개값이 Child 타입인지 여부를 instanceof 연산자로 확인한다. Child 타입이 아니라면 강제 타입 변환을 할 수 없기 때문이다. 강제 타입 변환을 하는 이유는 Child 객체의 모든 멤버에 접근하기 위해서이다.

public void method(Parent parent) {
	if(parent instanceof Child) {
    Child child = (Child) parent;
    // child 변수 사용
    }
  }

 

Java 12부터는 instanceof 연산의 결과가 true일 경우, 우측 타입 변수를 사용할 수 있기 때문에 강제 타입 변환이 필요 없다.

if(parent instanceof Child child) {
	// child 변수 사용
    }

'Language > JAVA' 카테고리의 다른 글

[JAVA] 봉인된 클래스  (0) 2024.09.04
[JAVA] 추상 클래스  (1) 2024.09.04
[JAVA] 상속  (0) 2024.09.04
[JAVA] 싱글톤 패턴  (0) 2024.09.04
[JAVA] Getter와 Setter  (0) 2024.09.04