Today Yewon Learned

[JAVA] 상속 본문

JAVA

[JAVA] 상속

데브워니 2021. 11. 11. 15:54

상속(extends, Inheritance)

- 상속이란 부모 클래스(상위 클래스)와 자식 클래스(하위 클래스)가 있으며, 자식 클래스는 부모 클래스를 선택해서 그 부모의 멤버를 상속받아 그대로 쓸 수 있음

- 중복된 코드 줄임 → 유지보수 편리

- 통일성

- 다형성

- 단, 상속을 하더라도 자식 클래스가 부모의 모든 것들을 물려받는 것은 아님

  • 부모 클래스의 private 접근 제한을 갖는 필드 및 메소드 자식이 물려받을 수 없음
  • 부모와 자식 클래스가 서로 다른 패키지에 있다면, 부모의 default 접근 제한을 갖는 필드 및 메소드도 자식이 물려받을 수 없음
    (default 접근 제한은 ‘같은 패키지에 있는 클래스’만 접근이 가능하게끔 하는 접근 제한자이기 때문)
  • 그 이외의 경우는 모두 상속의 대상이 됨

 

클래스 상속

- 상속받고자 하는 자식 클래스명 옆에 extends 키워드를 붙이고, 상속할 부모 클래스명을 적음
- 자바는 다중 상속을 허용하지 않으므로, extends 뒤에는 하나의 부모 클래스만 와야 함

public Class Parent{ .... }; // 부모 클래스
public Class Child extends parent { .... }; // 자식 클래스

 

@Override(오버라이딩)

오버로드란, 상위 클래스에 정의된 메소드의 이름, 반환형, 매개변수 선언까지 완전히 동일한 메소드를 하위 클래스에서 다시 정의한다면, 하위 클래스의 해당 메소드가 상위 클래스의 메소드를 덮어버리는(가려버리는, 지워버리는) 것

 

super

1. 자식클래스가 부모 클래스의 변수, 메소드를 사용할 때 사용

public class BankAccount {
    .
    .
    .
    boolean withdraw(int amount) {
        ...
    }
}
public class TransferLimitAccount extends BankAccount {
    private int transferLimit;

    @Override
    boolean withdraw(int amount) {
        if (amount > transferLimit) {
            return false;
        }

        return super.withdraw(amount);
    }
}

자식 클래스의 withdraw는 부모 클래스의 withdraw 메소드를 오버라이드 했는데, 정의 내에서 부모 클래스의 withdraw 메소드를 사용함.

이렇게 부모 클래스의 메소드(또는 변수)를 사용할 때는 super를 사용하면 됨

 

2. 자식클래스가 부모 클래스의 생성자를 사용할 때 사용

public class BankAccount {
    ...
    public BankAccount(int balance) {
        this.balance = balance;
    }
}
public class TransferLimitAccount extends BankAccount {
    ...
    public TransferLimitAccount(int balance, int transferLimit) {
        super(balance);
        this.transferLimit = transferLimit;
    }
}

자식 클래스의 인스턴스가 생성되기 위해서는 부모 클래스와 자식 클래스의 초기 설정을 모두 해주어야 함.

따라서 부모 클래스의 생성자도 불려야 함.

부모 클래스의 생성자는 super 키워드를 사용해서 부를 수 있음.

전에 this를 사용해서 생성자를 부르는 것과 같은 개념.

 

super 생성자 사용 규칙

public class Parent {
    ...
}

public class Child extends Parent {
    ...
}

(1) 먼저, 자식 클래스의 인스턴스 생성시 부모 클래스의 생성자는 반드시 불림

Child c = new Child(); // Child 인스턴스 생성시 Parent의 생성자도 불림

인스턴스의 생성을 위해선 생성자가 반드시 필요함.

자식 클래스의 인스턴스를 만들기 위해선 자식 클래스의 생성자는 물론이고, 부모 클래스의 생성자도 불려야 합니다. 상속이 여러 단계로 되면 맨 위쪽까지 다 불리게 됨.

 

만약 부모 클래스에 명시된 생성자가 없으면 자동으로 제공되는 부모 클래스의 기본 생성자가 불림

 

(2) 또, 부모 클래스에 기본 생성자가 없는 경우, 즉, 파라미터가 없는 생성자가 없는 경우, 자식 클래스에서 반드시 직접 (코드로 써서) 부모 클래스의 생성자 호출을 시켜주어야 함

public class Parent {
    // 별도로 생성자가 지정되어있는 경우 (파라미터 없는 기본 생성자가 없는 경우)
    public Parent(int a, int b) {
        ...
    }
    ...
}
    
public class Child extends Parent {
    public Child() {
        super(0, 0); // 자식 클래스에 생성자를 만들어 'super(int, int)'를 호출해 주어야 함
        ...
    }
}

(3) 그리고 그렇게 부모 클래스의 생성자를 호출할 때는 자식 클래스의 생성자 맨 윗줄에 적어야 함. 마치 부모 클래스의 생성자가 불린 다음 자식 클래스의 생성자가 불린다고 생각할 수 있음

public class Child extends Parent {
    public Child() {
        int a = 0;
        ...
        super(0, 0); // 이 곳에서 호출할 수 없음. 반드시 첫 번째 줄에 있어야 함.
    }
}

 

 

 

public class A {
    public A() {
        System.out.println("1");
    }

    public void a() {
        System.out.println("2");
    }
}

예제1)

public class B extends A {
    // empty class!
}

B b = new B();
b.a();

결과 값)

1
2

자식 클래스의 인스턴스 생성 시 부모 클래스의 생성자는 반드시 불리게 됨.

자식 클래스에서 따로 명시하지 않은 경우 부모 클래스(A)의 기본 생성자(A())가 불리기 때문에 "1"이 먼저 출력 됨

그 후 b.a()를 호출함

B는 A의 메소드를 상속받기 때문에 A의 a메소드가 호출되어 "2"가 출력 됨

 

 

예제2)

public class C extends A {
    public void a() {
        System.out.println("3");
    }
}

C c = new C();
c.a();

결과 값)

1
3

예제1과 같은 이유로 "1"이 먼저 출력 됨

뒤이어 c.a()의 결과는 C클래스에서 A클래스의 a메소드를 오버라이드 했기 때문에, A의 a메소드가 호출되지 않고,

C의 a메소드가 호출되어 "3"이 출력 됨

 

예제3)

public class D extends C {
    public void a() {
        System.out.println("4");
        super.a();
    }
}

D d = new D();
d.a();

결과 값)

1
4
3

예제1과 같은 이유로 "1"이 먼저 출력 됨

부모 클래스의 생성자가 불릴 때, 부모의 부모 생성자까지 모두 호출되기 때문에 A의 생성자까지 호출 됨

 

그리고 D가 부모 클래스인 C의 a 메소드를 오버라이드 했기 때문에 D의 a메소드가 호출됨

따라서 "4"가 출력되고 super.a()가 불림

super.a()는 부모 클래스인 C의 a메소드이기 때문에 "3"이 출력 됨

 

 

접근 제어자

종류 클래스 패키지 하위클래스 전체
public O O O O
protected O O O X
default O O X X
private O X X X

 

 

public class BankAccount {
    private int balance;
    ...
}
public class MinimumBalanceAccount extends BankAccount {
    ...
    @Override
    public boolean withdraw(int amount) {
        if (getBalance() - amount < minimum) {
            System.out.println("적어도 " + minimum + "원은 남겨야 합니다.");
            return false;
        }
    
        setBalance(getBalance() - amount);
        return true;
    }
}

자식 클래스 MinimumBalanceAccount에서는 private 변수인 balance에 대한 권한이 없기 때문에, public 메소드인 getBalance와 setBalance 메소드를 사용함

보호 차원에서는 좋지만, 상속한 클래스에서의 접근성마저 떨어져서 유연하지 못하고 불편함

 

protected 접근 제어자

protectedprivate과 유사하지만 자식 클래스에선 접근 가능하도록 함.

상속의 유연함과 private의 보호성을 동시에 만족시켜주는 키워드

public class BankAccount {
    protected int balance;
    ...
}
public class MinimumBalanceAccount extends BankAccount {
    ...
    @Override
    public boolean withdraw(int amount) {
        // if (getBalance() - amount < minimum) {
        if (balance - amount < minimum) {
            System.out.println("적어도 " + minimum + "원은 남겨야 합니다.");
            return false;
        }
    
        // setBalance(getBalance() - amount);
        balance -= amount;
        return true;
    }
}

그럼 이제 자식 클래스에서 balance 변수에 바로 접근 가능

상속받는 클래스에서만 접근 가능하나, BankDriver같은 외부 클래스에서 접근이 안 되는 것은 여전히 유효함

상속받은 클래스에서의 접근이 잦을 경우, 내부 변수를 private이 아닌 protected로 선언해야할 때가 꽤 있는데, 부모 클래스나 자식 클래스 모두 스스로 작성한다면 상황에 따라 유연하게 바꾸어가며 작성할 수 있음

 

Object 클래스

최상위 클래스, 모든 클래스의 부모 클래스

package org.opentutorials.javatutorials.progenitor;
 
class O {}

위의 코드는 아래와 코드가 같다.

package org.opentutorials.javatutorials.progenitor;
 
class O extends Object {}

자바에서 모든 클래스는 사실 Object를 암시적으로 상속받고 있는 것이다. 그런 점에서 Object는 모든 클래스의 조상이라고 할 수 있다. 그 이유는 모든 클래스가 공통으로 포함하고 있어야 하는 기능을 제공하기 위해서다.

 

Person 클래스에 아무 것도 작성하지 않았는데, person뒤에 많은 메소드가 뜨는 이유는 Object 클래스를 암시적으로 상속받고 있기 때문이다.

 

[참조] : codeit , 생활코딩

'JAVA' 카테고리의 다른 글

[JAVA] Generic(제네릭)  (0) 2021.11.15
[JAVA] Casting(캐스팅 : 형변환)  (0) 2021.11.12
[JAVA] Setter 메소드와 Getter 메소드  (0) 2021.11.10
[JAVA] HashMap  (0) 2021.11.10
[JAVA] ArrayList  (0) 2021.11.10
Comments