Java의 상속

둥냥둥냥둥·2023년 6월 15일
0
post-thumbnail

수직적 구조와 수평적 구조의 설계 방법

상속이란?

상속은 계층화를 통해 부모, 자식이라는 관계를 수직적으로 설계하는 기술이다.

공통된 부분을 Animal 클래스에서 구현하고 자식은 해당 동작을 쉽게 가져다 쓸 수 있도록 extends를 통해서 상속관계를 만들어주는 원리이다.

자바에서 최상위 Root 클래스는 Object 클래스이다.
눈에 보이지 않지만 모든 클래스에는 부모로 명시된 extends Object가 붙어 있다.또한, super()도 눈에 보이지 않는 기본 생성자로 사용되므로 하나의 클래스가 실행될 때 super()는 자식이 아닌 부모부터 생성하기 때문에 Object가 생성되고, Animal이 생성된다. Animal을 생성한 뒤에 Dog를 생성하는 것도 마찬가지이다.

[ Object - Animal - Dog == Cat ] 이런 상속체이닝 구조로 구성되어 있다.


Q. 어떤 데이터를 담아야 하는데 나에게 바구니 없다면 다른 사람이 가지고 있는 바구니를 사용하면 된다.

그런데 나와 다른 사람은 아무런 관계가 없기 때문에 다른 사람의 바구니를 사용하지 못한다.
하지만 나의 부모를 생각 해보자 부모에게 바구니가 있으면 자식은 부모의 바구니를 사용 할 수 있다.(허락해 주면,상속해 주면)

자식과 부모는 상속 관계이기 때문에 자식은 부모의 것을 얼마든지 사용 가능하다.

상속 (수평적 | 수직적) 설계

수평적 설계(기본적인 설계)

DogCat
eat()eat()
(1)
public class Dog {
    public void eat() {
        System.out.println("개는 먹는다");
    }


public class Cat {
    public void eat() {
        System.out.println("고양이는 먹는다");
    }
(2)
ublic class Basic {
    public static void main(String[] args) {
 
    Dog dog = new Dog();
    dog.eat();
 
    Cat cat = new Cat();
    cat.eat();
 
   }
}
  • 클래스 설계시 Dog, Cat를 만든다고 가정을 하면 eat()라는 행위는 중복된다.
  • Dog에도 eat(), Cat에도 eat() 등 모든 동물에 eat()를 따로 만든다는 건데이런 설계는 수도 많아지면 관리가 어렵고 수정하기도 쉽지 않다.

수직적 설계(계층화 | 상속구조)

Animal
DogCat
eat()eat()
(1)
public class Animal {
    public void eat() {
        System.out.println("동물은 먹는다.");
    }
}
(2)
public class Dog extends Animal {

}

public class Cat extends Animal {
}
(3)
public class Basic {
    public static void main(String[] args) {
 
       자식으로 받음        
    // Dog dog = new Dog();
    // dog.eat();
     
       부모로 받음
    // 부모-자식간의 자동 형변환, 업캐스팅이 된다.
    Animal ani = new Dog();
    ani.eat();
        
    ani = new Cat();
    ani.eat();   }
}

만약 Dog, Cat의 중복적인 속성을 밖으로 빼서 하나의 클래스로 만들면 어떨까.
즉, 이들의 부모인 Animal에만 eat()를 만들어서 상속한다면 코드는 복잡해지지만 유지보수 측면에서 훨씬 이점이 많을 것이다.

상속을 할 때는 자식 클래스에서 extends를 붙이는데 '나를 확장해서 부모까지 영향을 행사한다' 정도로 볼 수 있다. 이 클래스를 사용할 때는 보통 자식 클래스에서 생성하여 받는 것보다 부모 클래스에서 생성한다.


Override

상속의 재정의(Override)란

상속받은 자식 클래스가 부모 클래스의 동작을 수정하는 것
=> (2)에서 아쉬운 점을 찾아보면 나는 Dog는 "개는 먹는다." Cat은 "고양이는 먹는다."

 라고 표현하고 싶은데 공통점으로 묶는다는 이유로 "동물은 먹는다."로 썼다.

 재정의를 하지 않으면 부모가 가진 내용을 변화없이 오직 그대로 쓰게된다.

자식으로 받음 : Dog클래스의 모든 동작을 알고 있는 경우

  1. Dog dog = new Dog();로 객체를 만들면 super()를 통해

    부모인 Animal() 객체가 메모리에 먼저 생성된 뒤에 Dog()객체가 생성된다.

  2. Dog() 객체는 상속받아 확장된 상태기 때문에 Animal까지 접근가능한 Dog타입의 객체라 볼 수 있다.

    dog가 메모리를 가리키게 될 때 당연히 Animal ~ Dog까지의 범위에 도달할 수 있다.

  3. 만약 재정의를 할 경우 같은 메서드라면 Animal이 무시되고 Dog의 메서드가 실행된다.

부모로 받음 : Dog클래스의 동작 방식을 몰라서 부모에 의존하는 경우

  1. Animal ani = new Dog();로 객체를 만들면 마찬가지로 super()를 통해 부모인 Animal() 객체가 메모리에 먼저 생성된 뒤에 Dog()객체가 생성된다.

  2. Dog() 객체는 Animal타입이기 때문에 ani가 메모리를 가리키게 될 때 Animal의 범위에만 도달할 수 있다.

  3. 만약 재정의를 할 경우 같은 메서드라면 재정의를 했는지 찾아가게 되어있고 재정의가 되어있을 경우에는 Dog의 메서드가 실행된다. (동적 바인딩)

    • ani.eat(); 의 컴파일 시점에서는 부모의 eat()인데 실행될 때는 동적 바인딩을 통해서 자식에게 재정의된 메서드를 찾고 Dog()에 eat()가 재정의되어 있다면 그걸 선택한다.
  4. 즉, Dog의 기능을 몰라도 부모의 타입으로 Dog의 메서드를 재정의할 수 있다.

 public class Dog extends Animal {
 
    @Override
    public void eat() {
        System.out.println("개는 먹는다.");
    }
}

public class Cat extends Animal {
 
    @Override
    public void eat() {
        System.out.println("고양이는 먹는다.");
    }
}

수직적 설계의 내용과 다 동일하며 DogAnimal의 내용을 재정의해주면 된다.

down casting

만약 자식의 메서드 중에 재정의 되지 않은 메서드는 부모타입으로 만들 때 어떻게 해야 할까.

public class Cat extends Animal {
 
    @Override
    public void eat() {
        System.out.println("고양이는 먹는다.");
    }
    
    public void night() {
        System.out.println("고양이는 야행성이다.");
    }
}

이런 경우 ani = new Cat(); ani.night();로 하면 에러가 난다.

이럴 때 다운 캐스팅이 필요하다. 부모의 타입을 자식의 타입으로 형변환 해야 한다.

((Cat)ani).night();

바로 이렇게 다운 캐스팅을 강제적으로 해주면 된다.

출처 : https://yulfsong.tistory.com/51
profile
Java 먹자

0개의 댓글