뭐라도 끄적이는 BLOG

Java 상속(Inheritance) 본문

Java/Java 기본

Java 상속(Inheritance)

Drawhale 2023. 7. 2. 00:12

자바 상속의 특징

상속

상속(Inheritance)이란 기존의 클래스에 기능을 추가하거나 재정의한 새로운 클래스를 정의하는 것을 의미한다. 상속을 이용하면 기존에 정의되어 있는 클래스의 모든 필드와 메소드를 물려받아, 새로운 클래스를 생성할 수 있다. 이때 기존에 정의되어 있던 클래스를 부모 클래스(parent class) 또는 상위 클래스(super class), 기초 클래스(base class)라고도 한다. 그리고 상속을 통해 새롭게 작성되는 클래스를 자식 클래스(child class) 또는 하위 클래스(sub class), 파생 클래스(derived class)라고도 한다.

class 자식클래스이름 extend 부모클래스이름 { ... }

자식 클래스에는 부모 클래스의 필드와 메소드만이 상속되며, 생성자와 초기화 블록은 상속되지 않는다. 또한, 부모 클래스의 접근 제어가 private이나 default로 설정된 멤버는 자식 클래스에서 상속받지만 접근할 수는 없다. 

Java에서 클래스는 단 한 개의 클래스만을 상속받는 단일 상속만이 가능합니다.

Object Class

자바에서 Object 클래스는 모든 클래스의 부모 클래스가 되는 클래스이다. 자바의 모든 클래스는 자동으로 Object 클래스의 모든 필드와 메소드를 상속받게 된다. 즉, 자바의 모든 클래스는 별도로 extends 키워드를 사용하여 Object 클래스의 상속을 명시하지 않아도 Object 클래스의 모든 멤버를 자유롭게 사용할 수 있다. 자바의 모든 객체에서 toString()이나 clone()과 같은 메소드를 바로 사용할 수 있는 이유가 해당 메소드들이 Object 클래스의 메소드이기 때문이다.

super 키워드

super 키워드는 부모 클래스로부터 상속받은 필드나 메소드를 자식 클래스에서 참조하는 데 사용하는 참조 변수이다. 인스턴스 변수의 이름과 지역 변수의 이름이 같을 경우 인스턴스 변수 앞에 this 키워드를 사용하여 구분할 수 있었다. 이와 마찬가지로 부모 클래스의 멤버와 자식 클래스의 멤버 이름이 같을 경우 super 키워드를 사용하여 구별할 수 있다. 이렇게 자바에서는 super 참조 변수를 사용하여 부모 클래스의 멤버에 접근할 수 있다. this와 마찬가지로 super 참조 변수를 사용할 수 있는 대상도 인스턴스 메소드뿐이며, 클래스 메소드에서는 사용할 수 없다.

class Parent { int a = 10; }
 
class Child extends Parent {
    void display() {
        System.out.println(a);
        System.out.println(this.a);
        System.out.println(super.a);
    }
}

위의 예제에서 int형 변수 num는 부모 클래스인 Parent 클래스에서만 선언되어 있다. 따라서 지역 변수와 this 참조 변수 그리고 super 참조 변수 모두 같은 값을 출력한다.

class Parent {
    int a = 10;
}

class Child extends Parent {
    int a = 20;
 
    void display() {
        System.out.println(a);
        System.out.println(this.a);
        System.out.println(super.a);
    }
}

위의 예제에서 int형 변수 num는 자식 클래스인 Child 클래스에서도 선언되어 있다. 따라서 지역 변수와 this 참조 변수는 자식 클래스에서 대입된 값을 출력하며, super 참조 변수만이 부모 클래스에서 대입된 값을 출력한다.

super()

this() 메소드가 같은 클래스의 다른 생성자를 호출할 때 사용된다면, super() 메소드는 부모 클래스의 생성자를 호출할 때 사용한다. 자식 클래스의 인스턴스를 생성하면, 해당 인스턴스에는 자식 클래스의 고유 멤버뿐만 아니라 부모 클래스의 모든 멤버까지도 포함되어 있다. 따라서 부모 클래스의 멤버를 초기화하기 위해서는 자식 클래스의 생성자에서 부모 클래스의 생성자까지 호출해야 한다. 이러한 부모 클래스의 생성자 호출은 모든 클래스의 부모 클래스인 Object 클래스의 생성자까지 계속 거슬러 올라가며 수행된다. 따라서 자바 컴파일러는 부모 클래스의 생성자를 명시적으로 호출하지 않는 모든 자식 클래스의 생성자 첫 줄에 자동으로 다음과 같은 명령문을 추가하여, 부모 클래스의 멤버를 초기화할 수 있도록 해준다.

super();

메소드 오버라이딩

오버라이딩(overriding)이란 상속 관계에 있는 부모 클래스에서 이미 정의된 메소드를 자식 클래스에서 같은 시그니쳐를 갖는 메소드로 다시 정의하는 것이다. 자바에서 자식 클래스는 부모 클래스의 private 멤버를 제외한 모든 메소드를 상속받는다. 이렇게 상속받은 메소드는 그대로 사용해도 되고, 필요한 동작을 위해 재정의하여 사용할 수도 있다.

  • 오버라이딩이란 메소드의 동작만을 재정의하는 것이므로, 메소드의 선언부는 기존 메소드와 완전히 같아야 한다. 하지만 메소드의 반환 타입은 부모 클래스의 반환 타입으로 타입 변환할 수 있는 타입이라면 변경할 수 있다.
  • 부모 클래스의 메소드보다 접근 제어자를 더 좁은 범위로 변경할 수 없다.
  • 부모 클래스의 메소드보다 더 큰 범위의 예외를 선언할 수 없다.
class Parent {
    void display() { 
        System.out.println("부모 클래스의 display() 메소드입니다."); 
    }
}
class Child extends Parent {
    void display() {
        System.out.println("자식 클래스의 display() 메소드입니다.");
    }
}

다이나믹 메소드 디스패치 (Dynamic Method Dispatch)

static dispatch는 컴파일 타임에 어떤 메소드가 호출될지 알고 정해지는 것을 의미한다. 반면 dynamic dispatch는 컴파일타임에는 알 수 없는 메소드의 의존성을 런타임에 바인딩 하는 것이다. Interface 혹은 Abstract Class에서 정의된 abstract method를 호출하는 경우 발생한다.

Static Dispatch

public class StaticDispatch {
    void run(){
        System.out.println("run");
    }
    void run(String message){
        System.out.println(message);
    }

    public static void main(String[] args) {
        new StaticDispatch().run();
    }
}

main을 실행하게 되면 클래스에 정의된 run메소드중 어느 것이 실행되는지 컴파일 시점에서 알수 있다.

Dynamic Dispatch

interface Dispatch{
    void run();
}

class DynamicDispatch implements Dispatch{

    @Override
    public void run() {

    }
}

class DynamicDispatch2 implements Dispatch{

    @Override
    public void run() {

    }
}
public class DispatchClass{
    public static void main(String[] args) {
        Dispatch d1 = new DynamicDispatch();
        Dispatch d2 = new DynamicDispatch2();
        d1.run();
    }
}

d1과 d2는 컴파일 시점에 기존 Dispatch인터페이스의 run()을 호출하면 된다고 인식합니다. 그리고 런타임에 컴파일러가 가지고 있는 타입의 정보를 가지고 자기가 어떤 메소드를 실행해야 하는지 결정한다.

상속 혹은 구현된 모든 모든 클래스에서 메서드를 오버라이딩하면 Dynamic Dispatch는 발생한다.

Double Dispatch

interface Member{
    void getBenefit(Benefit benefit);
}
class SilverMember implements Member{

    @Override
    public void getBenefit(Benefit benefit) {
        benefit.getPoint(this);
    }
}
class GoldMember implements Member {

    @Override
    public void getBenefit(Benefit benefit) {
        benefit.getPoint(this);
    }
}

interface Benefit{
    void getPoint(SilverMember silverMember);
    void getPoint(GoldMember goldMember);
}

class BenefitImpl implements Benefit{

    @Override
    public void getPoint(SilverMember silverMember) {
        System.out.println("Silver Point");
    }

    @Override
    public void getPoint(GoldMember goldMember) {
        System.out.println("Gold Point");
    }
}

public class VisitorTest {
    public static void main(String[] args) {
        Member silver = new SilverMember();
        Member gold = new GoldMember();
        BenefitImpl b = new BenefitImpl();
        silver.getBenefit(b);
    }
}

새로운 등급이 추가되어도 해당 부분만 추가하면 된다.

Visitor pattern

Visitor pattern, 방문자 패턴, 실제 로직을 가지고 있는 객체(Visitor)가 로직을 적용할 객체(Element) 방문하면서 실행하는 패턴이다. 즉, 로직과 구조를 분리하는 패턴이라고 볼 수 있다. 로직과 구조가 분리되면 구조를 수정하지 않고도 새로운 동작을 기존 객체 구조에 추가할 수 있다.

추상 클래스

자바에서는 하나 이상의 추상 메소드를 포함하는 클래스를 가리켜 추상 클래스(abstract class)라고 한다.

추상 메소드

추상 메소드(abstract method)란 자식 클래스에서 반드시 오버라이딩해야만 사용할 수 있는 메소드를 의미한다. 자바에서 추상 메소드를 선언하여 사용하는 목적은 추상 메소드가 포함된 클래스를 상속받는 자식 클래스가 반드시 추상 메소드를 구현하도록 강제하기 위해서 이다. 이러한 추상 메소드는 선언부만이 존재하며, 구현부는 작성되어 있지 않다. 작성되어 있지 않은 구현부를 자식 클래스에서 오버라이딩하여 사용하는 것이다.

abstract 반환타입 메소드이름();

추상 클래스는 동작이 정의되어 있지 않은 추상 메소드를 포함하고 있으므로, 인스턴스를 생성할 수 없다. 추상 클래스는 먼저 상속을 통해 자식 클래스를 만들고, 만든 자식 클래스에서 추상 클래스의 모든 추상 메소드를 오버라이딩하고 나서야 비로소 자식 클래스의 인스턴스를 생성할 수 있게 된다. 추상 클래스는 추상 메소드를 포함하고 있다는 점을 제외하면, 일반 클래스와 모든 점이 같다.

abstract class 클래스이름 {
    ...
    abstract 반환타입 메소드이름();
    ...
}

final 키워드

대상 의미
클래스 다른 클래스의 부모클래스로 사용할 수 없습니다. (상속 금지)
메소드 오버라이딩을 할 수 없습니다.
멤버 변수 값을 변경할 수 없습니다. (상수화)
지역 변수

 

반응형

'Java > Java 기본' 카테고리의 다른 글

Java Interface  (0) 2023.07.02
Java 패키지  (0) 2023.07.02
Java Class  (0) 2023.07.01
Java 제어문  (0) 2023.07.01
Java 연산자  (0) 2023.07.01