STUDY/JAVA
[Java] 이것이 자바다 Ch.07
Eunjng
2024. 1. 13. 21:48
'이것이 자바다' 교재 및 강의를 참고하여 정리한 내용입니다.
Ch.07 상속
상속 개념
- 상속: 부모가 자식에게 물려주는 행위
- 중복 코드를 줄여 개발 시간 단축
- 클래스의 수정 최소화
클래스 상속
public class 자식클래스 extends 부모클래스 { }
- 다중 상속 비허용
부모 생성자 호출
- 자식 객체를 생성하면 부모 객체가 먼저 생성된 다음에 자식 객체가 생성된다.
- 모든 객체는 생성자를 호출해야만 생성되고, 부모 생성자는 자식 생성자의 맨 첫 줄에 숨겨져 있는 super()에 의해 호출된다.
public 자식클래스(...) {
super();
...
}
메소드 재정의
- 부모 클래스의 어떤 메소드가 자식 클래스가 사용하기에 적합하지 않은 경우, 자식 클래스에서 해당 메소드를 재정의해서 사용해야 한다.
- 메소드 오버라이딩: 상속된 메소드를 자식 클래스에서 재정의하는 것
- 해당 부모 메소드는 숨겨지고, 자식 메소드가 우선적으로 사용
- 주의해야 할 규칙
- 부모 메소드의 선언부(리턴 타입, 메소드 이름, 매개변수)와 동일해야 한다.
- 접근 제한을 더 강하게 오버라이딩할 수 없다(public -> private 변경 불가)
- 새로운 예외를 throws 할 수 없다.
- 주의해야 할 규칙
- 해당 부모 메소드는 숨겨지고, 자식 메소드가 우선적으로 사용
@Override
어노테이션: 컴파일 단계에서 정확히 오버라이딩 되었는지 체크하고, 문제 시 컴파일 에러 출력
final 클래스와 final 메소드
- final 클래스
- 부모 클래스가 될 수 없어 자식 클래스를 만들 수 없다.
- final 메소드
- 자식 클래스에서 재정의할 수 없다.
protected 접근 제한자
- protected는 같은 패키지에서는 default처럼 접근 가능하나, 다른 패키지에서는 자식 클래스만 접근을 허용한다.
- 필드와 생성자, 메소드 선언에 사용될 수 있다.
// package1
public class A {
protected String field; // 필드 선언
protected A() {} // 생성자 선언
protected void method() {} // 메소드 선언
}
// package2
import ch07.sec06.package1.A;
public class D extends A {
public D() {
super(); // A() 생성자 호출
}
public void method1() {
// A 필드값 변경 및 메소드 호출 가능(상속)
this.field = "value";
this.method();
}
public void method2() {
// 아래처럼 직접 객체 생성해서 사용하는 것은 불가
// A a = new A();
// a.field = "value";
// a.method;
}
}
타입 변환
- 클래스의 타입 변환은 상속 관계에 있는 클래스 사이에서 발생
부모타입 변수 = 자식타입객체;
일 때 자동 타입 변환 발생
class Animal {...}
class Cat extends Animal {...}
// Cat 객체를 생성하고 Animal 변수에 대입하면 자동 타입 변환
Cat cat = new Cat();
Animal animal = cat;
// Animal animal = new Cat();도 가능
cat == animal // true
- 부모 타입으로 자동 타입 변환된 이후에는 부모 클래스에 선언된 필드와 메소드만 접근 가능. 변수는 자식 객체를 참조하지만 변수로 접근 가능한 멤버는 부모 클래스 멤버로 한정된다.
- 그러나 자식 클래스에서 오버라이딩된 메소드가 있다면 부모 메소드 대신 오버라이딩된 메소드가 호출 - 다형성
강제 타입 변환
- 부모 타입은 자식 타입으로 자동 변환되지 않고, 캐스팅 연산자로 강제 타입 변환을 할 수 있다.
자식타입 변수 = (자식타입) 부모타입객체;
- 자식 객체가 부모 타입으로 자동 변환된 후 다시 자식 타입으로 변환할 때 강제 타입 변환 사용 가능
Parent parent = new Child(); // 자동 타입 변환
Child child = (Child) parent; // 강제 타입 변환
- 자식 타입에 선언된 필드와 메소드를 사용해야 한다면 강제 타입 변환을 해서 다시 자식 타입으로 변환해야 한다.
다형성
- 다형성: 사용 방법은 동일하지만 실행 결과가 다양하게 나오는 성질
- 다형성 구현을 위해서는 자동 타입 변환과 메소드 재정의가 필요
필드 다형성
- 필드 타입은 동일하지만, 대입되는 객체가 달라져서 실행 결과가 다양하게 나올 수 있는 것
// package1
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() 메소드 호출
tire.roll();
}
}
public class CarExample {
public static void main(String[] args) {
// Car 객체 생성
Car myCar = new Car();
// Tire 객체 장착
myCar.tire = new Tire();
myCar.run(); // 회전합니다
// HankookTire 객체 장착
myCar.tire = new HankookTire();
myCar.run(); // 한국 타이어가 회전합니다
// KumhoTire 객체 장착
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 drive(Vehicle vehicle) {
vehicle.run();
}
}
public class DriverExample {
public static void main(String[] args) {
// Driver 객체 생성
Driver driver = new Driver();
// 매개값으로 Bus 객체를 제공하고 driver() 메소드 호출
Bus bus = new Bus();
driver.drive(bus); // 버스가 달립니다
// driver.drive(new Bus());와 동일
Taxi taxi = new Taxi();
driver.drive(taxi); // 택시가 달립니다
}
}
객체 타입 확인
- instanceof 연산자를 사용해 변수가 참조하는 객체의 타입 확인
public void method(Parent parent) {
// parent 매개변수가 참조하는 객체가 Child인지 조사
// Child 타입이 아니라면 강제 타입 변환을 할 수 없기 때문
if(parent instanceof Child) {
Child child = (Child) parent;
// child 변수 사용
}
}
// Java 12부터는 instanceof 연산의 결과가 true일 경우 우측 타입 변수를 사용할 수 있기 때문에 강제 타입 변환 불필요
if(parent instanceof Child child) {
// child 변수 사용
}
추상 클래스
- 객체를 생성할 수 있는 클래스를 실체 클래스라고 한다면, 이 클래스들의 공통적인 필드나 메소드를 추출해서 선언한 클래스를 추상 클래스라고 한다.
- 추상 클래스는 실체 클래스의 부모 역할을 하기 때문에, 실체 클래스는 추상 클래스를 상속해서 공통적인 필드나 메소드를 물려받을 수 있다.
- 추상 클래스는 new 연산자를 사용해서 객체를 직접 생성할 수 없다.
- 추상 클래스는 새로운 실체 클래스를 만들기 위한 부모 클래스로만 사용된다. 즉, extends 뒤에만 올 수 있다.
추상 클래스 선언
public abstract class 클래스명 {...}
- 추상 클래스도 필드, 메소드를 선언할 수 있고 생성자가 반드시 있어야 한다.
public abstract class Phone {
// 필드 선언
String owner;
// 생성자 선언
Phone(String owner) {
this.owner = owner;
}
// 메소드 선언
void turnOn() {
System.out.println("폰 전원을 켭니다.");
}
void turnOff() {
System.out.println("폰 전원을 끕니다.");
}
}
public class SmartPhone extends Phone {
SmartPhone(String owner) {
// Phone 생성자 호출
super(owner);
}
void internetSearch() {
System.out.println("인터넷 검색을 합니다.");
}
}
public class PhoneExample {
public static void main(String[] args) {
// Phone phone = new Phone(); -> 불가
SmartPhone smartPhone = new SmartPhone("홍길동");
smartPhone.turnOn(); // 폰 전원을 켭니다.
smartPhone.internetSearch(); // 인터넷 검색을 합니다.
smartPhone.turnOff(); // 폰 전원을 끕니다.
}
}
추상 메소드와 재정의
- 자식 클래스들이 가지고 있는 공통 메소드를 뽑아내어 추상 클래스로 작성할 때, 메소드 선언부만 동일하고 실행 내용은 자식 클래스마다 달라야 하는 경우가 있다.
- 이 경우를 위해 추상 클래스는 다음과 같은 추상 메소드를 선언할 수 있다.
abstract 리턴타입 메소드명(매개변수, ...);
- 추상 메소드는 자식 클래스에서 반드시 재정의해서 실행 내용을 채워야 한다.
public abstract class Animal {
public void breathe() {
System.out.println("숨을 쉽니다");
}
public abstract void sound();
}
public class Dog extends Animal {
@Override
public void sound() {
System.out.println("멍멍");
}
}
봉인된 클래스
- final 클래스를 제외한 모든 클래스는 부모 클래스가 될 수 있지만, Java 15부터는 무분별한 자식 클래스 생성 방지를 위해 봉인된(sealed) 클래스가 도입되었다.
public sealed class Person permits Employee, Manager {...}
- permits 키워드 뒤에 상속 가능한 자식 클래스 지정
- 봉인된 Person 클래스를 상속하는 Employee와 Manager는 final 또는 non-sealed 키워드로 선언하거나, sealed 키워드를 사용해서 또 다른 봉인 클래스로 선언해야 한다.
public final class Employee extends Person {...}
public non-sealed class Manager extends Person {...}
- final은 더 이상 상속 불가, non-sealed는 봉인 해제
- Employee는 더 이상 자식 클래스를 만들 수 없지만 Manager는 자식 클래스를 만들 수 있다.