[Java] 6장_상속

22406 단어 자바자바

6장_상속

목표

자바의 상속에 대해 학습한다.

목차

  1. 자바 상속 개요
  2. 상속의 필요성
  3. 클래스 상속
  4. 인터페이스 상속
  5. this와 super

Ref: WhiteShip 라이브 스터디 6주차 과제

1. 자바 상속 개요

Inheritance, 상속

상속은 객체 지향 프로그래밍(OOP)의 핵심 원칙 중 하나이다. 이를 통해 기존 코드를 재사용하거나 기존 타입을 확장할 수 있다.

상속의 핵심은 클래스와 인터페이스에 있다.
클래스는 다른 클래스와 여러 인터페이스를 상속할 수 있고, 인터페이스는 다른 인터페이스를 상속할 수 있다. 이 장에서는 상속의 필요성부터 시작해 클래스와 인터페이스에서 상속이 어떻게 이루어지는지에 대해 다룬다. 그 다음 변수 또는 메서드 이름과 접근 제한자가, 상속 멤버에게 어떤 영향을 미치는지도 살펴본다. 마지막으로 타입을 상속한다는 것이 무엇을 의미하는지 살펴본다.

2. 상속의 필요성

자동차 제조업체가 고객에게 여러 자동차 모델을 제공한다고 해보자. 선루프나 통풍시트 등 자동차 모델에 따라 제공되는 옵션(또는 기능)이 다를 수 있다. 하지만 모든 자동차라면 엔진과 바퀴라는 공통 요소와 라이트 작동 등의 공통 기능을 가지고 있다.

디자인 관점에서, 처음부터 각각의 자동차 모델을 개별적으로 디자인하는 것보다, 기본 디자인을 만들고 확장하여 특수 버전을 만드는 것이 좋다.

비슷한 방식으로 상속을 사용하여 기본 기능과 동작이있는 클래스를 만들고, 이 기본 클래스를 상속하는 클래스를 만들어 특수 버전을 만들 수 있다. 같은 방식으로 인터페이스는 기존 인터페이스를 확장할 수 있다.

다른 타입, 특히 상속 관계의 타입을 참조하기 위해 아래의 용어들을 사용한다.

  • 상위(super) 또는 부모(parent) 타입: 기본 디자인
  • 확장(extended), 하위(sub) 또는 자식(child) 타입: 파생 디자인(파생 타입)

3. 클래스 상속

클래스 확장

클래스는 다른 클래스를 상속하고 추가 멤버를 정의할 수 있다. 아래 Car 기본 클래스를 정의하였다.

public class Car {
    int wheels;
    String model;
    void start() {
        // 시작을 위한 필수 부품 확인 과정
    }
}

ArmoredCar 클래스는 클래스 선언시 extends 키워드를 사용하여 Car 클래스의 멤버를 상속할 수 있다.

public class ArmoredCar extends Car {  // 선언시 extends 키워드를 사용하여 상속한다
    int bulletProofWindows;  // 이거 방탄 유리ㅇ!.. 할 때 방탄 창문
    
    // 이 차량은 원격 제어를 사용하여 시작할 수 있다
    void remoteStartCar() {
    
    }
}

이를 통해 CarArmoredCar의 수퍼 클래스이고, ArmoredCarCar의 하위 클래스라고 말할 수 있다.

자바의 클래스는 단일 상속을 지원한다. 따라서 ArmoredCar 클래스는 여러 클래스를 확장할 수 없다.

단, extends 키워드가 없는 경우, 클래스는 묵시적으로 java.lang.Object 클래스를 상속한다.

하위 클래스는 수퍼 클래스에서 non-static protected 멤버 및 public 멤버를 상속받을 수 있다. 또한 두 클래스가 동일한 패키지에 있는 경우, default 멤버 및 패키지 접근 권한이 있는 멤버를 상속받을 수 있다. 반면, 클래스의 private 멤버 및 static 멤버는 상속받을 수 없다. 이 내용을 정리하면 아래 표와 같다.

접근 제어자상속 가능 여부 (수퍼 클래스 -> 하위 클래스)
non-static protectedO
publicO
default동일 패키지 내 상속 가능
(패키지 접근이 가능한 멤버들도)
privateX
staticX

하위클래스에서 접근

상속이 이루어진 속성 또는 메소드에 접근할 때에는, 그냥 해당 멤버를 직접 사용하면 된다.

public class ArmoredCar extends Car {
    public String registerModel() {
        return model;
    }
}

멤버에 접근하기 위해 수퍼 클래스를 참조하지 않아도 된다.

4. 인터페이스 상속

다중 인터페이스 구현

클래스는 단 한 개의 클래스만 상속할 수 있지만, 인터페이스는 여러 개의 인터페이스를 구현할 수 있다.

어떤 스파이 한 명이 이전에 정의한 ArmoredCar 를 필요로 한다고 생각해보자. 이를 위해 자동차 제조 회사는 자동차가 물 위에서 떠다니거나(Floatable) 비행할 수 있는(Flyable) 기능을 추가할 수 있다.

public interface Floatable {
    void floatOnWater();
}
public interface Flyable {
    void fly();
}
public class ArmoredCar extends Car implements Floatable, Flyable {
	public void floatOnWater() {
        System.out.println("물 위에 뜰 수 있어요!");
    }
    
    public void fly() {
        System.out.println("날 수 있어요!");
    }
}

위의 예에서 인터페이스 상속을 위해 implement 키워드를 사용하는 것을 알 수 있다.

다중 상속 문제

자바 7까지는 다중 상속 문제가 발생하지 않았다. 인터페이스는 오직 abstract 메소드만 만들 수 있었고, 이 메소드는 구현이 완성된 것이 아니었다. 그래서 하나의 클래스가 여러 인터페이스를 구현하고, 동일한 메소드 시그니처를 가진다 하더라도 문제가 되지 않았다. 결국 구현된 클래스는 단 하나의 구현 메소드를 가졌기 때문이다.

그런데 자바 8부터는 바뀐 부분이 있다. 이제 인터페이스 메소드의 구현 방식을 선택할 수 있게 되었다. (abstract 메소드로 작성해도 되고, 인터페이스 메소드 내부에서 구현을 완성해도 된다.)

만약 동일한 메소드 시그니처를 갖는 인터페이스들이 있고, 이를 구현하는 클래스가 있다고 하자. 해당 클래스는 같은 시그니처의 메소드 중 어떤 메소드를 구현해야 할까?

이 문제와 관련해, 자바는 별도의 인터페이스에 동일 메소드 시그니처가 정의된 경우, 상속을 허용하지 않고 있다. 예를 들어 보자.

public interface Floatable {
    default void repair() {
    	System.out.println("Repairing Floatable object");	
    }
}
public interface Flyable {
    default void repair() {
    	System.out.println("Repairing Flyable object");	
    }
}
public class ArmoredCar extends Car implements Floatable, Flyable {
    // this won't compile
}

두 인터페이스를 모두 구현하려면 아래와 같이 repair() 메소드를 override (재정의)해야 한다.

public class ArmoredCar implements Floatable, Flyable {
    @Override // 없어도 무방
    public void repair() {
    	System.out.println("Repairing Floatable and Flyable object");	
    }
}

앞의 예에서 인터페이스가 동일한 이름 (예 : duration)을 가진 변수를 정의하는 경우, 변수 이름 앞에 인터페이스 이름을 추가하지 않으면 접근할 수 없다.

public interface Floatable {
    int duration = 10;
}
public interface Flyable {
    int duration = 20;
}
public class ArmoredCar extends Car implements Floatable, Flyable {
 
    public void printDurations() {
    	System.out.println(Floatable.duration); // outputs 10
    	System.out.println(Flyable.duration); // outputs 20
    	System.out.println(duration); // won't compile (The field duration is ambiguous)
    }
}

인터페이스 확장

하나의 인터페이스는 여러 인터페이스를 확장할 수 있다. 아래 예시를 보자.

public interface Floatable {
    void floatOnWater();
}
public interface Flyable {
    void fly();
}
public interface SpaceTraveller extends Floatable, Flyable {
    void remoteControl();
}

인터페이스는 extends 키워드를 사용하여 다른 인터페이스를 상속한다.

5. this와 super

슈퍼 클래스와 서브 클래스가 동일한 이름의 변수나 메소드를 정의한다면 어떻게 될까?

자바에서는 this 또는 super 키워드를 사용해 각각에 접근할 수 있다. 아래 예시를 살펴보자.

public class ParentCar {
    public String type;

    public ParentCar() {
        this.type = "Super";
    }
}
public class ChildCar extends ParentCar {
    public String type;

    public ChildCar() {
        this.type = "Child";

        System.out.println("1. With keyword 'this': " + this.type);
        System.out.println("2. With keyword 'super': " + super.type);
        System.out.println("3. Without keywords 'this or super': " + type);
    }

    public static void main(String[] args) {
        ChildCar c = new ChildCar();
    }
}


// 출력 결과
// 1. With keyword 'this': Child
// 2. With keyword 'super': Super
// 3. Without keywords 'this or super': Child

Ref

좋은 웹페이지 즐겨찾기