Skip to content

Latest commit

 

History

History
242 lines (169 loc) · 5.21 KB

아이템20-추상-클래스보다는-인터페이스를-우선하라.md

File metadata and controls

242 lines (169 loc) · 5.21 KB
  1. 추상 클래스, 인터페이스 두 메커니즘 비교
  • 공통점:
    • 타입을 정의하기 위한 다중 구현 메커니즘
    • 인스턴스 메소드를 구현 형태로 제공
  • 차이점:
    • 추상 클래스:
      • 추상 클래스가 정의한 타입을 구현하는 클래스는 반드시 추상 클래스의 하위 클래스 -> 새로운 타입 정의에 제약
    • 인터 페이스:
      • 어떤 클래스 타입에 있건 인터페이스를 정의한 클래스는 해당 인터페이스 타입 가능 취급

-> 여러 인터페이스를 구현할 수 있다.

-> 하나의 추상 클래스만 상속받을 수 있다.


  1. 인터페이스는 믹스인 정의에 맞춤이다.

믹스인: 주된 타입 외의 선택적 행위를 제공

가장 적절한 예는 Comparable, Serializable 을 사용한 예이다.

public class Rank implements Comparable<Rank>, Serializable {
    
    @Override
    public int compareTo(Rank o) {
        return 0;
    }
}

  1. 계층이 없는 타입 프레임워크가 가능하다.
public interface Comic {

    void laugh();
}

public interface Action {

    void fight();
}

public interface Melo {

    void love();
}

public interface ActionComedy implements Comic, Action {

    void slapstick();
}

인터페이스의 경우 ActionComedy 라는 인터페이스를 정의한 클래스라면 모두 ActionComedy 타입으로 취급할 수 있다.

  • 추상 클래스로 구현
public abstract class Comic {

    void laugh();
}

public abstract class Action {

    void fight();
}

public abstract class Melo {

    void love();
}

public abstract class ActionComedy {
    
    void laugh();
    void fight();
    void slapstick();
}

조합 폭발(combinatorial explosion)이 발생한다.


  1. 디폴트 메소드에는 제약이 있다.
  • 제약 사항
    1. Object 메소드인 equals와 hashcode를 디폴트 메소드로 제공 안함.

    2. 인터페이스는 인스턴스 필드를 가질 수 없고 public이 아닌 정적 메소드를 가질 수 없음.

      a. 후니: JAVA 9 부터 private static void method() {} 가능!

    3. 본인이 만든 인터페이스가 아니면 디폴트 메소드를 추가할 수 없음.


  1. 골격 구현(skeletal implementation) 활용

인터페이스와 추상 클래스의 장점을 모두 취하는 방법

  • 인터페이스로 타입을 정의, 필요하면 디폴트 메소드도 제공
  • 골격 구현 클래스가 나머지 메소드들을 구현
public interface Athlete { 
    
    void 근육을_키우자(); 
    void 지구력을_기르자(); 
    void 연습하자(); 
    void 루틴();
}

public class SoccerPlayer implements Athlete { 
    
    @Override 
    void 근육을_키우자() { 
        System.out.println("헬스장 레츠고"); 
    }

    @Override 
    void 지구력을_기르자() { 
        System.out.println("청계천 러닝"); 
    }

    @Override 
    void 연습하자() { 
        System.out.println("슈팅 연습"); 
    }

    @Override
    void 루틴() {
        근육을_키우자();
        지구력을_기르자();
        연습하자();
    }

}

public class BaseballPlayer implements Athlete {
    
    @Override
    void 근육을_키우자() {
        System.out.println("헬스장 레츠고");
    }

    @Override
    void 지구력을_기르자() {
        System.out.println("청계천 러닝");
    }

    @Override
    void 연습하자() {
        System.out.println("배팅 연습");
    }

    @Override
    void 루틴() {
        근육을_키우자();
        지구력을_기르자();
        연습하자();
    }
}
public interface Athlete {

    void 근육을_키우자();

    void 지구력을_기르자();

    void 연습하자();

    void 루틴();
}

public abstract class WangsimniAthlete implements Athlete {
    
    @Override
    public void 근육을_키우자() {
        System.out.println("헬스장 레츠고");
    }

    @Override
    public void 지구력을_기르자() {
        System.out.println("청계천 러닝");
    }

    @Override
    public void 루틴() {
        근육을_키우자();
        지구력을_기르자();
        연습하자();
    }
}

public class SoccerPlayer extends WangsimniAthlete implements Athlete {

    @Override
    public void 연습하자() {
        System.out.println("슈팅 연습");
    }
}

public class BaseballPlayer extends WangsimniAthlete implements Athlete {

    @Override
    public void 연습하자() {
        System.out.println("배팅 연습");
    }
}

public class Main {
    
  public static void main(String[] args) {

      List<Athlete> athletes = new ArrayList<>();
      Athlete soccerPlayer = new SoccerPlayer();
      Athlete baseballPlayer = new BaseballPlayer();
      athletes.add(soccerPlayer);
      athletes.add(baseballPlayer);
  
      for (Athlete athlete : athletes) {
        athlete.루틴();
      }
  }
}

디폴트 메소드를 사용하지 않고 추상 골격 구현 클래스(AbstractCharacter)를 구현하여 중복을 없앨 수 있다.

일반적으로 다중 구현용 타입으로는 인터페이스가 가장 적절하며 재사용성 측면이나 유연성 측면 그리고 다형성 측면에서 인터페이스를 우선하는 것이 옳다.