Backend

<Backend> Java / 상속

이게왜 2024. 4. 5. 15:28

상속 (Inheritance)

 

이번에는 상속에 대해 알아보겠습니다.

 

상속은 객체지향 프로그래밍(OOP)에서 중요한 개념입니다.

Java에서 상속은 하나의 클래스가 다른 클래스의 속성과 메서드를 물려받는 것을 말합니다.

상속은 코드를 재사용하고 클래스 간에 계층 구조를 형성하는 데 사용됩니다.

 

* 목차

  • 상속의 특징, 장점, 단점
  • 클래스 상속
  • 인터페이스 상속
  • 다형성 (Polymorphism)

상속의 특징

  • 계층 구조 형성 : 상속을 통해 클래스 간에 계층 구조를 형성하여 부모 클래스와 자식 클래스들을 관리할 수 있다.
  • 코드 재사용 : 부모 클래스의 속성과 메서드를 자식 클래스에서 재사용할 수 있어 중복코드를 줄이고 유지보수성을 높일 수 있다.
  • 다형성 지원 : 상속을 통해 다형성을 구현할 수 있다.

상속의 장점

  • 코드 재사용 : 부모 클래스의 속성과 메서드를 자식 클래스에서 재사용할 수 있어 중복코드를 줄이고 유지보수성을 높일 수 있다.
  • 다형성 지원 : 상속을 통해 다형성을 구현할 수 있다.

상속의 단점

  • 과도한 계층 구조 : 지나치게 복잡한 계층 구조가 형성될 경우, 코드의 가독성과 유지보수가 어려워질 수 있다.
  • 의존성 문제 : 자식 클래스는 부모 클래스의 의존하게 되며, 부모 클래스의 변경이 자식 클래스에 영향을 미칠 수 있다.
  • 오용 : 상속을 오용하면 적절하지 않은 관계가 형성될 수 있으며, 코드의 복잡성을 증가시킬 수 있다.

클래스 상속 (Class Inheritance)

자바에서 클래스 상속은 한 클래스가 다른 클래스의 속성과 메서드를 물려받는 것을 의미합니다.

이를 통해 코드의 재사용성을 높이고, 클래스 간의 계층 구조를 형성할 수 있습니다.

 

  • extends 키워드 사용
  • 단일 상속만 가능: 하나의 자식클래스는 하나의 부모클래스만 가질 수 있다.
  • 메서드 오버라이딩 가능 : 자식 클래스는 부모 클래스의 메서드를 재정의할 수 있다.
  • 다형성 구현
// 부모 클래스
class Animal {
    String species;

    public void eat() {
        System.out.println("동물이 먹는다.");
    }

    public void makeSound() {
        System.out.println("동물이 소리를 만듭니다.");
    }
}

// 자식 클래스
class Dog extends Animal {
    public void bark() {
        System.out.println("멍멍!");
    }

    @Override                // 메서드 오버라이딩
    public void makeSound() {
        System.out.println("멍멍!");
    }
}

// 자식 클래스
class Cat extends Animal {
    public void meow() {
        System.out.println("야옹~");
    }

    @Override
    public void makeSound() {
        System.out.println("야옹~");
    }
}

public class Main {
    public static void main(String[] args) {
        Dog dog = new Dog();
        dog.species = "개";
        dog.eat(); // Animal 클래스의 eat 메서드 호출
        dog.makeSound(); // Dog 클래스의 makeSound 메서드 호출
        dog.bark(); // Dog 클래스의 bark 메서드 호출

        Cat cat = new Cat();
        cat.species = "고양이";
        cat.eat(); // Animal 클래스의 eat 메서드 호출
        cat.makeSound(); // Cat 클래스의 makeSound 메서드 호출
        cat.meow(); // Cat 클래스의 meow 메서드 호출
    }
}

 

  • Animal Class는 동물의 종을 나타내는 필드와 eat() 메서드를 가지고 있습니다.
  • Dog Class와 Cat Class는 Animal Class를 상속받았으며, 각각 bark()와 meow() 메서드를 추가하였습니다.
  • Dog Class와 Cat Class는 Animal Class를 상속받았으며, Animal Class의 makeSound() 메서드를 재정의 하였습니다. (overriding)
  • 하위 클래스는 extends 키워드를 사용하여 상위 클래스를 상속받으며, 상위 클래스의 필드와 메서드를 사용할 수 있습니다.

인터페이스 상속 (Interface Inheritance)

인터페이스 상속은 클래스가 인터페이스로부터 메서드 시그니처를 물려받는 것을 의미합니다.

또한 인터페이스 간의 상속이 가능하며, extends 키워드를 사용합니다.

 

  • 다중상속 : 인터페이스는 다른 인터페이스로부터 다중 상속을 받을 수 있습니다.
  • 다형성 구현

** 단, 인터페이스를 상속받은 클래스는 인터페이스에서 선언된 모든 메서드를 반드시 구현해야 한다.

 

interface Shape {
    void draw();
}
class Circle implements Shape {
    public void draw() {     // 오버라이딩
        System.out.println("원을 그립니다.");
    }
}

class Rectangle implements Shape {
    public void draw() {        // 오버라이딩
        System.out.println("사각형을 그립니다.");
    }
}
public class Main {
    public static void main(String[] args) {
        Shape shape1 = new Circle();
        Shape shape2 = new Rectangle();

        shape1.draw(); // "원을 그립니다." 출력
        shape2.draw(); // "사각형을 그립니다." 출력
    }
}

다형성 (Polymorphism)

다형성(polymorphism)이란 하나의 객체가 여러 가지 타입을 가질 수 있는 것을 의미합니다.

자바에서는 이러한 다형성을 부모 클래스 타입의 참조 변수로 자식 클래스를 참조할 수 있도록 하여 구현합니다.

 

다형성은 상속, 추상화와 더불어 객체 지향 프로그래밍을 구성하는 중요한 특징 중 하나입니다.


참조 변수의 다형성

  • 부모 클래스 타입의 참조 변수로 자식 클래스를 참조할 수 있다.

이때, 참조 변수가 사용할 수 있는 멤버의 개수가 실제 인스턴스의 멤버 개수보다 같거나 적어야 참조할 수 있습니다.

class Parent { ... }

class Child extends Parent { ... }

...

Parent pa = new Parent(); // 허용

Child ch = new Child();   // 허용

Parent pc = new Child();  // 허용

Child cp = new Parent();  // 오류 발생.

참조 변수의 타입 변환

자바에서는 참조 변수도 다음과 같은 조건에 따라 타입 변환을 할 수 있습니다.

 

  1. 서로 상속 관계에 있는 클래스 사이에만 타입 변환을 할 수 있다.
  2.  자식 클래스 타입에서 부모 클래스 타입으로의 타입 변환은 생략할 수 있다.

단, 부모 클래스 타입에서 자식 클래스 타입으로의 타입 변환은 반드시 명시해야 합니다.

class Parent { ... }

class Child extends Parent { ... }

class Brother extends Parent { ... }

...

Parent pa01 = null;

Child ch = new Child();

Parent pa02 = new Parent();

Brother br = null;

 

pa01 = ch;          // pa01 = (Parent)ch; 와 같으며, 타입 변환을 생략할 수 있음.

br = (Brother)pa02; // 타입 변환을 생략할 수 없음.

br = (Brother)ch;   // 직접적인 상속 관계가 아니므로, 오류 발생.

instanceof 연산자

이러한 다형성으로 인해 런타임에 참조 변수가 실제로 참조하고 있는 인스턴스의 타입을 확인할 필요성이 생깁니다.

자바에서는 instanceof 연산자를 제공하여, 참조 변수가 참조하고 있는 인스턴스의 실제 타입을 확인할 수 있도록 해줍니다.

참조변수 instanceof 클래스이름

 

// 동물 클래스
class Animal {
    public void makeSound() {
        System.out.println("동물이 소리를 냅니다.");
    }
}

// 강아지 클래스
class Dog extends Animal {
    public void makeSound() {
        System.out.println("멍멍!");
    }
}

// 고양이 클래스
class Cat extends Animal {
    public void makeSound() {
        System.out.println("야옹~");
    }
}

public class InstanceOfExample {
    public static void main(String[] args) {
        Animal myDog = new Dog();
        Animal myCat = new Cat();

        // instanceof 연산자를 사용하여 객체의 타입을 확인
        if (myDog instanceof Dog) {
            System.out.println("myDog은 Dog 클래스의 인스턴스입니다.");
        }

        if (myDog instanceof Animal) {
            System.out.println("myDog은 Animal 클래스의 인스턴스입니다.");
        }

        if (myCat instanceof Cat) {
            System.out.println("myCat은 Cat 클래스의 인스턴스입니다.");
        }

        if (myCat instanceof Animal) {
            System.out.println("myCat은 Animal 클래스의 인스턴스입니다.");
        }
    }
}
myDog은 Dog 클래스의 인스턴스입니다.
myDog은 Animal 클래스의 인스턴스입니다.
myCat은 Cat 클래스의 인스턴스입니다.
myCat은 Animal 클래스의 인스턴스입니다.