Java

[Java] 자바 상속의 개념과 예시

고즈너키 2023. 5. 31. 14:00

Intro

안녕하세요! Plitche(플리체)입니다. :P
자바에서 상속은 객체 지향 프로그래밍의 핵심 개념 중 하나입니다. 상속은 클래스 간의 계층적인 관계를 구성하여 코드의 재사용성을 높이고, 유지 보수를 용이하게 하는 기능을 제공합니다. 이를 통해 새로운 클래스를 기존 클래스의 특성과 동작을 상속받아 새로운 기능을 추가하거나, 기존의 기능을 변경할 수 있습니다.

상속을 이해하기 위한 중요 개념

1. 슈퍼클래스(superclass), 부모클래스(parent class)

  • 상속을 해주는 클래스를 슈퍼클래스 또는 부모클래스라고 부릅니다. 슈퍼클래스는 상속된 멤버들을 가지고 있으며, 이를 서브클래스에게 제공합니다.

2. 서브클래스(subclass), 자식클래스(child class)

  • 상속을 받는 클래스를 서브클래스 또는 자식클래스라고 부릅니다. 서브클래스는 슈퍼클래스로부터 멤버들을 상속받아 사용할 수 있습니다.

예시

상속을 위해 extends 키워드를 사용합니다. 예를 들어, 다음과 같이 Vehicle이라는 슈퍼클래스가 있고 Car라는 클래스가 Vehicle을 상속받는다고 가정해봅시다.

class Vehicle {
    // 멤버 변수
    String brand;
    
    // 생성자
    public Vehicle(String brand) {
        this.brand = brand;
    }
    
    // 메소드
    public void drive() {
        System.out.println("Driving a vehicle");
    }
}

class Car extends Vehicle {
    // 서브클래스의 추가 멤버 변수
    int numberOfSeats;
    
    // 서브클래스의 추가 메소드
    public void park() {
        System.out.println("Parking the car");
    }
}

Car 클래스는 extends 키워드를 사용하여 Vehicle 클래스를 상속받았습니다. 이제 Car 클래스는 Vehicle 클래스의 멤버 변수와 메소드를 사용할 수 있습니다. Car 클래스는 numberOfSeats라는 추가적인 멤버 변수와 park()라는 추가적인 메소드를 가지고 있습니다.

Car myCar = new Car("Toyota");
myCar.drive();   // Vehicle 클래스의 drive() 메소드 호출
myCar.park();    // Car 클래스의 park() 메소드 호출

위의 예제에서 myCar 객체는 Car 클래스의 인스턴스이지만, Vehicle 클래스의 멤버들을 모두 사용할 수 있습니다. 
drive() 메소드는 Vehicle 클래스의 메소드이지만, myCar.drive()를 호출함으로써 Car 클래스의 인스턴스에서도 사용할 수 있습니다.

3. 오버라이딩

상속을 통해 서브클래스는 슈퍼클래스의 멤버들을 상속받을 뿐만 아니라, 필요에 따라 메소드를 재정의(오버라이딩)할 수도 있습니다. 예를 들어, Car 클래스에서 drive() 메소드를 재정의하여 자동차를 운전하는 방식을 변경할 수 있습니다.

class Car extends Vehicle {
    // ...

    // 메소드 오버라이딩
    @Override
    public void drive() {
        System.out.println("Driving a car");
    }
}


4. 생성자와 초기화 순서

서브클래스가 인스턴스화될 때, 슈퍼클래스의 생성자가 먼저 호출됩니다. 서브클래스의 생성자에서는 super 키워드를 사용하여 슈퍼클래스의 생성자를 명시적으로 호출할 수 있습니다. 이를 통해 슈퍼클래스의 초기화를 수행한 후에 서브클래스의 초기화를 진행할 수 있습니다.

class Vehicle {
    public Vehicle() {
        System.out.println("Vehicle constructor");
    }
}

class Car extends Vehicle {
    public Car() {
        super();  // 슈퍼클래스의 생성자 호출
        System.out.println("Car constructor");
    }
}

Car myCar = new Car();  // "Vehicle constructor" 출력 후 "Car constructor" 출력

 

5. 접근 제어자

상속 관계에서 멤버 변수와 메소드의 접근 제어는 중요한 요소입니다. 슈퍼클래스의 멤버 변수와 메소드에 대한 접근 제어자에 따라 서브클래스에서의 접근 가능성이 결정됩니다. 상속된 멤버에 접근하려면, 서브클래스에서 그 멤버에 접근 가능한 접근 제어자를 가져야 합니다.

 

 

6. 다형성(Polymorphism)

상속 관계에서 슈퍼클래스의 참조 변수로 서브클래스의 인스턴스를 참조하는 것을 다형성이라고 합니다. 이는 객체의 타입에 따라 다양한 동작이 수행될 수 있도록 합니다. 다형성을 통해 한 개의 변수로 여러 타입의 객체를 다룰 수 있으며, 런타임에 실제 객체의 타입에 따라 적절한 메소드가 호출됩니다.

class Vehicle {
    public void drive() {
        System.out.println("Driving a vehicle");
    }
}

class Car extends Vehicle {
    @Override
    public void drive() {
        System.out.println("Driving a car");
    }
}

Vehicle myVehicle = new Car();
myVehicle.drive();  // "Driving a car" 출력

위의 예제에서 myVehicle 변수는 Vehicle 타입이지만, Car 클래스의 인스턴스를 참조하고 있습니다. myVehicle.drive()를 호출하면 실제로 Car 클래스의 drive() 메소드가 실행되어 "Driving a car"라는 메시지가 출력됩니다.



자바 상속의 특성

  • 자바는 단일 상속만을 지원합니다. 즉, 한 클래스는 하나의 슈퍼클래스만을 가질 수 있습니다. 이러한 제한은 다중 상속으로 인해 발생할 수 있는 복잡성을 줄이고 클래스 간의 계층 구조를 단순화시키는 데 도움을 줍니다.

  • 상속은 클래스 간의 관계를 계층적으로 구성하여 코드의 재사용성과 유지 보수성을 높이는 중요한 개념입니다. 상속을 통해 기존 클래스를 수정하지 않고도 새로운 클래스를 만들어 추가 기능을 구현하거나 기존 기능을 변경할 수 있습니다.

주의사항

  1. 상속의 오용: 상속은 객체 지향 프로그래밍에서 강력한 도구이지만, 항상 상속을 사용해야 하는 것은 아닙니다. 클래스 간의 관계가 정말로 계층적이고 서로 다른 타입으로 구분될 필요가 있는 경우에만 상속을 사용해야 합니다. 때로는 인터페이스 구현이나 조합(composition)과 같은 다른 설계 방법이 더 적합할 수도 있습니다.

  2. 계층 구조의 복잡성: 너무 깊은 계층 구조를 가진 상속 구조는 코드의 이해와 유지 보수를 어렵게 할 수 있습니다. 계층 구조를 설계할 때는 적절한 수준의 추상화를 고려해야 합니다.

  3. 상속의 가시성: 서브클래스는 슈퍼클래스의 내부 구현에 의존할 수 있습니다. 이는 슈퍼클래스의 변경이 서브클래스에 영향을 미칠 수 있음을 의미합니다. 따라서 슈퍼클래스를 변경할 때는 이를 상속하는 모든 서브클래스에도 영향을 주는지 주의해야 합니다.