Home > Backend > Java > ☕️[Java] 상속

☕️[Java] 상속
Java Programming Language Backend

1️⃣ 상속.

1. 상속(Inheritance)

자바 프로그래밍에서의 상속(Inheritance)은 한 클래스가 다른 클래스의 속성과 메소드를 물려받는 기능을 말합니다.

상속을 사용하면 기존 코드를 재사용하고 확장하는 것이 용이해져, 소프트웨어의 설계와 유지 보수가 효율적으로 이루어질 수 있습니다.

1.2 상속의 주요 개념.

  • 1. 슈퍼클래스(부모 클래스) : 기능이 상속되는 클래스입니다.
    • 예를 들어, ‘Vehicle’ 클래스가 있을 때 클래스의 속성(예: 속도)과 메소드(예: start, stop)를 다른 클래스가 상속받을 수 있습니다.
  • 2. 서브클래스(자식 클래스) : 슈퍼클래스의 속성과 메소드를 상속받는 클래스입니다.
    • 서브클래스는 메소드를 그대로 사용할 수도 있고, 필요에 따라 재정의(오버라이드)할 수도 있습니다.
      • 예를 들어, ‘Car’ 클래스가 ‘Vehicle’ 클래스를 상속받는 경우, ‘Car’‘Vehicle’ 의 모든 속성과 메소드를 사용할 수 있으며 추가적인 기능(예: 4륜 구동 기능)을 더할 수 있습니다.
  • 3. 메소드 오버라이딩(Method Overriding) : 서브클래스가 슈퍼클래스에서 상속받은 메소드를 재정의하여 사용하는 것 입니다.
    • 서브클래스는 상속받은 메소드를 자신의 필요에 맞게 변경할 수 있습니다.
  • 4. 생성자 상속 : 자바에서 생성자는 상속되지 않습니다. 서브클래스의 생성자가 호출될 때, 슈퍼클래스의 생성자도 자동으로 호출되어야 하는데, 이는 ‘super()’ 키워드를 통해 명시적으로 호출해야 합니다.

📝 정리.

상속을 사용하면 코드의 중복을 줄이고, 각 클래스의 기능을 명확하게 구분지어 설계할 수 있어 프로그램 전체의 구조가 개선됩니다.


class 자식 클래스명 extends 부모 클래스명 { // 다중 상속 불가능
    필드;
    메소드;
    ...
}

2. 상속과 접근제어자와의 관계.

자바에서 상속과 접근 제어자(Access modifiers)는 클래스와 클래스 멤버(필드, 메소드)의 접근성을 결정하는 데 중요한 역할을 합니다.
접근 제어자는 클래스의 데이터를 보호하고, 코드의 유지 보수를 용이하게 하며, 외부로부터의 불필요한 접근을 막는 기능을 합니다.

상속에서 접근 제어자는 어떤 멤버가 서브클래스에게 상속될 수 있는지, 그리고 상속받은 멤버를 서브클래스가 어떻게 활용할 수 있는지 결정짓는 요소입니다.

2.1 주요 네 가지 접근 제어자.

  • 1. private : 멤버가 선언된 클래스 내에서만 접근 가능합니다.
    • ‘private’ 접근 제어자가 지정된 멤버는 상속되지 않습니다.
  • 2. default(package-private) : 접근 제어자를 명시하지 않으면, 기본적으로 ‘default’ 접근이 적용됩니다.
    • 이러한 멤버들은 같은 패키지 내의 다른 클래스에서 접근할 수 있지만, 다른 패키지의 서브클래스에서는 접근할 수 없습니다.
  • 3. protected : ‘protected’ 멤버는 같은 패키지 내의 모든 클래스와 다른 패키지의 서브클래스에서 접근할 수 있습니다.
    • 이 접근 제어자는 상속을 사용할 때 특히 유용하며, 서브클래스가 슈퍼클래스의 멤버를 활용하거나 수정할 수 있게 합니다.
  • 4, public : ‘public’ 멤버는 모든 클래스에서 접근할 수 있습니다.
    • 상속과 관련하여, ‘public’ 멤버는 서브클래스에 의해 자유롭게 상속되고 사용될 수 있습니다.

📝 정리.

  • 상속과 접근 제어자의 관계에서 중요한 점은, 서브클래스가 상속받은 멤버에 접근할 수 있는 권한은 슈퍼클래스에서 해당 멤버에 지정된 접근 제어자에 의해 결정된다는 것 입니다.
    • 예를 들어, 슈퍼클래스에서 ‘protected’ 로 선언된 메소드는 서브클래스에서 접근 가능하고 필요에 따라 오버라이딩할 수 있지만, ‘private’ 으로 선언된 메소드는 서브클래스에서 직접접으로 접근하거나 사용할 수 없습니다.
      • 이러한 제한은 객체 지향 프로그래밍에서 캡슐화와 정보 은닉을 강화하는 데 도움을 줍니다.

3. super와 super().

자바에서 ‘super’ 키워드와 ‘super()’ 생성자 호출은 상속을 사용할 때 매우 중요한 역할을 합니다.
이들은 서브클래스가 슈퍼클래스와 상호작용할 수 있게 해 줍니다.

3.1 super와 super() 키워드의 사용 방식.

3.1.1 super 키워드.

  • ‘super’ 키워드는 슈퍼 클래스의 필드나 메소드에 접근할 때 사용됩니다.
    • 서브클래스에서 메소드를 어버라이드 했을 때, 슈퍼클래스의 버전을 호출하고 싶은 경우에 유용하게 사용할 수 있습니다.
    • 이는 슈퍼클래스의 구현을 활용하면서 추가적인 기능을 서브클래스에 구현할 때 필요합니다.
      • 예를 들어, 슈퍼클래스 ‘Vehicle’ 의 메소드 ‘start()’ 를 서브클래스 ‘Car’ 에서 오버라이드한 후, ‘Car’‘start()’ 메소드에서 ‘super.start()’ 를 호출하면, ‘Vehicle’ 클래스의 ‘start()’ 메소드가 실행됩니다.

3.1.2 super() 생성자 호출.

  • ‘super()’ 는 서브클래스의 생성자에서 슈퍼클래스의 생성자를 호출할 때 사용됩니다.
    • 자바에서는 모든 클래스가 생성자를 가지며, 서브클래스의 생성자가 호출될 때 슈퍼클래스의 생성자도 자동으로 호출됩니다.
    • 명시적으로 슈퍼클래스의 생성자를 호출하고자 할 때 ‘super()’ 를 사용합니다.
  • 이 호출은 서브클래스의 생성자의 첫 번째 명령어로 위치해야 합니다.
    • 슈퍼클래스의 생성자를 호출함으로써, 슈퍼 클래스의 인스턴스 변수들이 적절히 초기화될 수 있습니다.
  • 예를 들어, 슈퍼클래스 ‘Vehicle’‘Vehicle(int speed)’ 라는 생성자가 있고, 서브클래스 ‘Car’ 에서 이를 상속 받을 때, ‘Car’ 의 생성자에서 ‘super(100)’ 을 호출하면 ‘Vehicle’ 의 생성자가 호출죄어 ‘speed’ 변수가 ‘100’ 으로 초기화됩니다.

📝 정리.

이 두 사용법은 객체지향 프로그래밍에서 클래스의 계층을 통해 기능을 확장하고 관리하는 데 필수적입니다.
‘super’ 의 사용은 상속 관계에 있는 클래스 간의 코드를 재사용하고, 유지 관리를 쉽게 하며, 다형성을 구현하는 데 중요한 역할을 합니다.


4. 오버라이딩(Overriding)

자바 프로그래밍에서 오버라이딩(Overriding)은 서브클래스가 상속받은 슈퍼클래스의 메소드를 자신의 요구에 맞게 재정의하는 과정을 말합니다.

오버라이딩은 객체 지향 프로그래밍의 핵심 개념 중 하나로, 다형성을 가능하게 하며, 상속 받은 메소드를 서브클래스에서 새로운 방식으로 구현할 수 있도록 해줍니다.

4.1 오버라이딩 규칙.

오버라이딩을 할 때는 몇 가지 규칙을 따라야 합니다.

  • 1. 메소드 이름과 시그니처 일치 : 오버라이딩할 메소드는 슈퍼클래스의 메소드와 동일한 이름, 매개변수 목록, 반환 타입을 가져야 합니다.
  • 2. 접근 제어 : 오버라이딩하는 메소드는 슈퍼클래스의 메소드보다 더 제한적인 접근 제어를 가질 수 없습니다.
    • 예를 들어, 슈퍼클래스의 메소드가 ‘public’ 이라면 서브클래스의 오버라이딩 메소드도 적어도 ‘public’ 이어야 합니다.
  • 3. 반환 타입 : 오버라이딩하는 메소드의 반환 타입은 슈퍼클래스의 메소드 반환 타입과 같거나 그 하위 타입이어야 합니다.(이것은 공변 반환 타입이라고 함.)

4.2 오버라이딩의 예.

슈퍼클래스 ‘Animal’ 에 다음과 같은 메소드가 있다고 가정해 봅시다.

public class Animal {
    public void makeSound() {
        System.out.println("Animal makes a sound");
    }
}

이제 ‘Dog’ 클래스가 ‘Animal’ 클래스를 상속받고 ‘makeSound()’ 메소드를 오버라이드하여 다음과 같이 구현할 수 있습니다.

public class Dog extends Animal {
    @Override // 이 어노테이션은 선택적이지만, 오버라이딩임을 명시적으로 나타냅니다.
    public void makeSound() {
        System.out.println("Dog barks");
    }
}
  • 이 예에서 ‘Dog’ 클래스의 ‘makeSound()’ 메소드는 ‘Animal’makeSound() 메소드를 오버라이드하여 “Dog barks”를 출력하도록 재정의합니다.

4.3 오버라이딩의 중요성.

오버라이딩은 다음과 같은 이점을 제공합니다.

  • 유연성 : 같은 메소드 호출이지만, 다양한 서브클래스에서 서로 다른 동작을 구현할 수 있습니다.
  • 재사용성 : 기존의 코드를 변경하지 않고도, 상속받은 메소드를 새로운 요구에 맞게 확장할 수 있습니다.
  • 유지보수 : 코드의 중복을 줄이고, 유지보수를 간편하게 할 수 있습니다.

📝 정리.

오버라이딩은 프로그램의 다형성을 구현하는 데 필수적인 기능으로, 상속받은 메소드를 사용하는 대신 서브클래스에 맞게 특화된 기능을 구현할 수 있도록 합니다.