티스토리 뷰

반응형

상속(Inheritance)과 구성(Composition)

상속을 사용하는 이유

  • 공통적인 부분을 가지고 있는 상위 클래스를 활용하여 하위 클래스는 본인 고유의 상태와 행동을 정의하기 위함이다.
  • 코드의 확장성, 재사용성이 용이하고 중복된 코드를 상위 클래스로 뺐으므로 코드가 간결해진다. 결과적으로, 유지보수가 쉬워진다는 점이다.

 

상속 정의

하위 클래스는 상위 클래스의 모든 메소드를 재사용할 수 있고, 재정의를 하여 하위 클래스만의 메소드로 변경할 수도 있다.

public class Car{
    public void print() {
        System.out.println("부릉부릉");
    }
}

public class Tico extends Car{
    @Override
    public void print() {
        System.out.println("티코티코");
    }

    public void print2() {
        System.out.println("소형차");
    }
}

public static void main(String[] args){
    Car ACar = new Car();
    ACar.print();
    //결과 : 부릉부릉

    Tico ATico = new Tico();
    ATico.print();  //재정의한 메소드 출력
    //결과 : 티코티코

    Car car = new Tico();    //하위 클래스 모든 메소드 사용 가능
    Tico tico = (Tico)car;    //tico는 car 참조 객체와 동일
    tico.print();
    //결과 : 티코티코
}

 

상속(Inheritance)과 구성(Composition)

상속의 단점

  • 상속은 단일 패키지에서 사용해야만 안전하다.
  • 하위 클래스는 상위 클래스에 많이 의존하게 된다.
  • 상위 클래스의 코드가 수정되면 하위 클래스의 코드도 수정되어야 하는 경우가 많다.(재정의 메소드)
  • 확장이라는 목표를 두고 상속을 사용하면 괜찮으나, 해당 설계가 아닌 상황에서 상속을 사용할 시 문제를 발생할 수 있다.
  • 상속은 캡슐화를 위반한다.
    • 하위 클래스가 상위 클래스에 구체적인 구현 내용을 의존하고 있기 때문이다.

 

소스를 통해 상속의 한계를 살펴보자

public class Calc {

    public int sum(int x, int y) {
        return x+y;
    }
}

public class Print extends Calc{
    public void print() {
        System.out.println(sum(1, 3));
    }
}
//1. 결과 : 4

//그러나 만약 Calc가 아래와 같이 변경된다면?
public class Calc {

    public int sum(int x, int y) {
        x = 4;
        return x+y;
    }
}

public class Print extends Calc{
    public void print() {
        System.out.println(sum(1, 3));
    }
}
//2. 결과: 7

하위 클래스는 상위 클래스에 의존하여 print()라는 함수를 통해 원하는 결과를 출력하고 싶은 것이다.

하지만, 상위 클래스의 메소드 변경으로 인해 하위 클래스는 원하는 결과값을 얻지 못하게 된다.


 

다음 소스도 살펴보자

public class Calc {

    public int sum(int x, int y) {
        return x+y;
    }

    public int minus(int x, int y) {
        return x-y;
    }
}

public class Print extends Calc{
    public String minus(int x, int y) {
        //이 부분은 컴파일 시 에러가 발생한다.
        //minus를 오버라이드하는데 상위 클래스는 int 타입으로 반환하여 불일치하기 때문이다.
    }

    public void print() {
        System.out.println(sum(1, 3));
    }
}

하위 클래스에서는 이미 minus라는 메소드로 운영을 하고 있는데, 상위 클래스의 새로운 메소드 추가로 하위 클래스가 컴파일 오류가 나타날 수 있는 것이다.


 

구성(Composition)으로 해결하자

  • Composition은 하위 클래스가 상위 클래스를 상속받는 것이 아니라 단순히 하위 클래스가 상위 클래스를 private으로 참조받아서 사용하는 것이다.
  • 그 후 상위 클래스에서 필요한 메소드를 가져다 사용하는 것이다. (포워딩 - wrapper class)
public class Calc {

    public int sum(int x, int y) {
        return x+y;
    }

    public int minus(int x, int y) {
        return x-y;
    }
}

public class Print{
    private Car car = new Car();
    public boolean minus(int x, int y) {
        return true;
    }

    public void print() {
        System.out.println("상위 클래스 : " + minus(5, 3) + ", 하위 클래스 : " + minus(5,3));
    }
}
//결과 : 상위 클래스 : 2, 하위 클래스 : true

 

정리

  • 상속은 상위 클래스와 하위 클래스가 순수한 is-a 관계일 때만 사용해야한다.
  • is-a 관계라고해서 무조건 사용하는 것이 아니라, 같은 패키지에 있어야 하며 상위 클래스가 확장을 고려하여 설계되어 있어야 한다.
  • 상속의 취약점을 피하고 싶다면 Composition을 사용해라.
  • Wrapper Class를 구현할 인터페이스가 있다면 Composition을 사용하기에는 너무 좋은 케이스이다.
반응형

'Java > Java 기초' 카테고리의 다른 글

[Java 기초] 추상클래스와 인터페이스  (0) 2020.01.05
[Java 기초] 접근제한자  (0) 2020.01.04
[Java 기초] 오버로딩  (0) 2020.01.03
[Java 기초] enum  (0) 2020.01.01
[Java 기초] 변수의 Scope와 static  (0) 2019.12.31
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/04   »
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30
글 보관함