[Java] 상속(Inheritance)



1️⃣ 상속(Inheritance)

  • 객체지향프로그래밍(OOP)3대특성중 하나입니다.
  • 부모클래스에서 확장하여 자식클래스로 진화(혹은 세분화)하는 개념으로 이해하시면 됩니다.
  • 컴포지션상속의 쓰임은 비슷합니다. 컴포지션으로 구현한 것을 상속으로 구현이 가능하며 그 반대도 가능합니다. 하지만 우리는 객체지향(OOP)적 관점에서 쓰임에 맞게 사용하는 것이 좋으며, 혹은 좀 더 효율적인 방법을 택하거나 회사의 방침에 맞춰 선택 해야할 것 입니다.


2️⃣ 상속 vs 컴포지션

  • 상속컴포지션의 기능은 비슷하기 때문에 둘중에 하나를 적절히 선택하여 사용해야 합니다. 일반적으로 선택하는 방법은 두클래스has-a관계인지 is-a관계인지를 따져보는 것 입니다.

(1) has-a관계

  • has-a관계한 클래스다른 클래스를 가지는(has) 관계입니다.
  • 예를들어 컴퓨터마우스, 키보드, 스피커, 모니터와 같은 관계입니다.
  • 이러한 관계에 있는 클래스는 컴포지션으로 사용하는 것이 일반적입니다.

(2) is-a관계

  • is-a관계자식 클래스부모 클래스이다(is)라고 말할 수 있는 관계입니다.
  • 예를들어 선생님과 수학선생님, 동물과 고양이와 같은 관계입니다. 수학선생님은 선생님이다., 고양이는 동물이다.라고 말할 수 있습니다.
  • 이러한 관계에 있는 클레스는 상속으로 사용하는 것이 일반적입니다.

(3) 상속의 상속(확장성)

  • 상속은 체인과같이 계속해서 확장해 나갈 수 있습니다. 즉, 자식클래스자식클래스를 만들 수 있습니다.
  • 위키피디아 - 생물의 종은 이러한 상속의 대표적인 예시입니다.


3️⃣ 상속의 사용

< 부모클래스(Animal) >

public class Animal {
    private int age;
    protected String name;   // protected제어자, 자식클래스에서 접근가능

    public Animal (int age) {
        this.age = age;
    }
    /* 코드 생략 */
}

< 자식클래스(Dog, Cat) >

/* Dog Class */
public class Dog extends Animal {

    public Dog(int age, String name) {
        super(age);
        super.name = name;
    }
}
/* Cat Class */
public class Cat extends Animal {

    public Cat(int age, String name) {
        super(age);
        this.name = name;   // super.가아닌 this.으로도 사용가능
    }
}
  • extends키워드를 사용해서 부모클래스를 지정해줄 수 있습니다.
  • 하나의 부모클래스여러개의 자식클래스를 가질 수 있습니다. 하지만 아직 Java나 C#에서는 여러개의 부모클래스를 가질 수 없습니다. (interface기능을 사용하여 가능하기는 함)**
  • 생성자를 호출할 때 부모클래스를 먼저 초기화해야 합니다. 이 때 super()를 이용하여 부모클래스를 초기화 해줍니다. (부모클래스의 생성자가 없거나 받는 매겨변수가 없을 경우 부모클래스의 초기화를 안해줘도 됨, 컴파일러가 자동으로 생성하여 실행)
  • 부모클래스에서 protected된 멤버변수는 자식클레스에서 접근이 가능합니다. super.으로 접근하지만 this.으로도 접근이 됩니다.


4️⃣ 잘 상속 하기(?)

(1) 부모클래스도 유효해야 한다(추상 클래스가 아닌경우)

  • OOP적인 관점에서 볼때 객체를 생성할 때 그 객체가 유효해야합니다.
  • 자식클래스부모클래스가 세트로 존재하는 것이 아닌 부모클래스자체만으로도 객체를 생성하고 존재하는 것이 좋습니다.
/* Animal Class*/
public class Animal {
    private int age;

    public Animal() {
    }
    public void setAge(int age) {
        this.age = age;
    }
}
/* Dog Class */
public class Dog extends Animal {
    public Dog(int age) {
        super.age = age;
    }
}
  • 위에서 Dog클래스는 생성과 동시에 나이를 가질 수있다. 하지만 Animal클래스를 이용하여 객체를 생성한다면 나이가 0살인 동물이 생성된다. (나중에 setAge()메소드를 이용해서 갑자기 나이가 생긴다..)
  • 객체독립적인 것이 좋으며 생성자의 멤버변수를 통해 생성과 동시에 유효한상태가 되게 만드는 것이 실수를 줄이는데에도 큰역할을 한다. (각 클래스의 멤버변수만 보고 어떤 것을 초기화를 해야 하는지 판단하는 것은 실수할 가능성이 크다.)
  • 부모클래스독자적으로 생기는 것이 불가능하다면(개념적으로나 여러가지이유 등..) 추상 클래스로 구현하는 것이 좋습니다.

(2) 무분별한 다형성(오버라이딩)의 사용

  • 다형성 또한 OOP의 3대특성에 있을 만큼 중요한 특성이며 상속이 있어야 사용이 가능합니다.
  • 하지만 무분별하게 다형성을 사용한다면 메모리의 낭비와 효율저하가 나타날 수있습니다.
  • 그렇기 때문에 다형성을 이용한 무조건적인 오버라이딩보다는 공통된 메서드일반화하여 부모클래스에 구현하는 것이 좋습니다. (기존 클래스에서 기능이 확장되어 자식클래스를 늘려가는 경우도 있지만 여러개의 클래스를 취합해서 역으로 부모클래스를 구성하는 경우도 있으며 이러한 경우 메소드들을 일반화하는 능력이 중요하다고 생각합니다.)


5️⃣ 상속관계에서의 형변환(type casting)

(1) 초기화에서 경우

Animal animal = new Dog();
Dog dog = new Animal();    // 컴파일 오류

(2) 일반적인 케스팅 경우

Animal animal = new Animal();
Dog dog = new Dog();
Cat cat = new Cat();

dog = (Dog) animal;   // 런타임 오류
dog = (Dog) cat;      // 컴파일 오류
animal = (Animal) dog;
  • 결과적으로 자식 클래스부모 클래스형변환을 할 수 있습니다.
  • 반대로 위의 코드 같은 경우 부모 클래스를 자식 클래스로 형변환할 수 없습니다. (컴파일은 가능하나 실행중에 에러가납니다.(런타임 오류))</rd>
  • Dog클래스와 Cat클래스처럼 부모, 자식관계에 있지않는 것들은 컴파일러에서 오류를 잡아줍니다.(컴파일 오류)

(3) 객체를 그 객체의 실체(클래스)로 케스팅하는 경우

/* case 1 */
Dog dog = new Dog();
Animal animal = dog;

Dog dog2 = (Dog) animal;
Cat cat = (Cat) animal;  // 런타임 오류
  • 이러한 경우 부모클래스자식클래스형변환하여 사용할 수 있습니다. (겉모습은 Animal이지만 Dog클래스)
  • 하지만 Cat클래스형변환하는 것은 런타임 오류가 생깁니다.

(4) 해결법(instancof 사용)

  • 위의 여러경우의수 처럼 겉모습이 Animal클래스로 되어 있다면 실제로 어떤 클래스가 케스팅되어 만들어진 것인지 알 수없는경우가 있습니다.
  • 이때 instanceof를 사용하면 됩니다. (boolean반환형)
  • 다음과 같이 조건문을 만들어 사용하면 런타임 오류를 방지할 수 있습니다.
Dog dog = new Dog();
Animal animal = dog;

if (animal instanceof Dog) {
    Dog dog2 = (Dog) animal;
}
if (animal instanceof Cat) {
    Cat cat = (Cat) animal;
}
  • instanceof그 객체의 클래스뿐만아니라 그 객체의 부모 클래스일때도 참(true)을 반환합니다.
Dog dog = new Dog();

System.out.println(dog instanceof Dog);     // true
System.out.println(dog instanceof Animal);  // true




© 2021.02. by kirim

Powered by kkrim