티스토리 뷰
(Effective Java) 규칙20. 추상 클래스 보다는 인터페이스를 우선하라
추상 클래스, 인터페이스
-
자바 언어에는 여러 가지 구현을 허용하는 자료형을 만드는 방법이 두가지 포함되어 있음.
- 인터페이스, 추상클래스(abstract class)
-
이 두 방법의 분명한 차이는 추상 클래스는 구현된 클래스를 포함할 수 있지만 인터페이스는 아니라는 것임.
- 자바 1.8 부터는 'default' 메서드를 통해 인터페이스에도 구현을 포함시킬 수 있음
-
좀 더 중요한 차이는 추상 클래스를 자료형으로 사용하기 위해서는 반드시 계승이 필요하다는 것이다.
- 인터페이스는 포함된 모든 메서드를 정의하고 인터페이스가 규정하는 일반 규약을 지키기만 하면됨
- 자바는 다중 상속(multiple inheritance)를 허용하지 않기 때문에 추상클래스를 사용하게 되면 자료형으로 사용하는데 많은 제약이 발생하게 됨
인터페이스의 이점
이미 있는 클래스를 개조해서 새로운 인터페이스를 구현하도록 하는 것은 간단하다.
-
필요한 메서드가 다 있는지 확인해서 없으면 추가한 다음 implements절을 추가하는 것이 전부
- 예) 자바 플랫폼에 Comparable 인터페이스가 추가된 이후, 많은 기존 클래스들이 Comparable을 구현하도록 개조됨
-
인터페이스는 믹스인(mixin)을 정의하는 데 이상적이다.
- 믹스인이란?
- 클래스가 "주 자료형(primary type)" 이외에 추가로 구현할 수 있는 자료형으로, 어떤 선택적 기능을 제공한다는 사실을 선언하기 위해 쓰임
- 예) Comparable은 자기 객체는 다른 객체와의 비교 결과에 따른 순서를 갖는다고 선언할 때 쓰는 믹스인 인터페이스
- 자료형의 주된 기능에 선택적인 기능을 "혼합(mix in)"할 수 있도록 함
- 믹스인이란?
인터페이스는 비 계층적인(nonhierarchical) 자료형 프레임워크(type frame-work)를 만들 수 있도록 한다.
-
어떤 것들은 자료형 계층(type hierachy)으로 잘 정리되지만, 계 안에 잘 들어맞지 않는 것들도 있음
-
예) 가수를 표시하는 인터페이스와 작곡가를 표현하는 인터페이스
public interface Singer { AudioClip sing(Song s); } public interface Songwriter { Song compose(boolean hit); }
- 문제는 가수 가운데 작곡가인 사람도 있음
- 추상 클래스 대신 인터페이스를 사용했기 때문에 아무런 문법적 문제 없이 Singer와 Songwriter를 확장한 또 다른 인터페이스를 추가할 수 있음
public interface SingerSongwriter extends Singer, Songwriter { //새로운 메서드를 추가할 수 있음 AudioClip strum(); void actSensitive(); }
인터페이스를 쓰지 않으려면 필요한 속성(attribute) 조합마다 별도의 클래스를 만들어 거대한 클래스 계층이 필요함
-
필요한 속성이 n개가 있다면 지원해야 하는 조합의 가짓수는 2^n개에 달할 것임
- 이런 문제는 조합 폭증(combinatorial explosion) 이라는 이름으로 알려져 있음
-
공통된 행위(behavior)를 모델링하는 자료형이 없으므로, 인자 자료형만 다른 메서들이 다수 포함된 거대한 클래스들이 자리하게 됨
인터페이스를 사용하면 포장 클래스 숙어(wrapper class idiom)을 통해 안전하면서도 강력한 기능 개선이 가능하다.
-
추상 클래스를 사용해 자료형을 정의하면 프로그래머는 계승 이외의 수단을 사용할 수 없음 (규칙 16)
-
추상 골격 구현 클래스를 중요 인터페이스마다 두면, 인터페이스의 장점과 추상 클래스의 장점을 결합할 수 있음
- 추상 골격 구현 클래스란?
- 인터페이스를 구현하는 데 필요한 노력을 최소화하기 위한 골격구현을 제공하는 클래스임
- 인터페이스로는 자료형을 정의하고, 구현하는 일은 골격구현 클래스에 맡기면 됨
- 예) 컬렉션 프레임워크에는 인터페이스별로 골격 구현 클래스들이 하나씩 제공됨
- AbstractCollection, AbstractSet, AbstractList, AbstractMap 등
- 예) 컬렉션 프레임워크에는 인터페이스별로 골격 구현 클래스들이 하나씩 제공됨
- 골격 구현의 강력함을 아주 잘 보여주는 예
//골격 구현 위에서 만들어진 완전한 List 구현 static List<Integer> intArrayAsList(final int[] a) { if (a == null) throw new NullPointerException(); return new AbstractList<Integer>() { public Integer get(int i) { return a[i]; //자동 객체화 } @Override public Integer set(int i, Integer val) { int oldVal = a[i]; a[i] = val; //자동 비객체화 return oldVal; //자동 객체화 } public int size() { return a.length; } }; }
- int 배열을 Integer 객체의 리스트처럼 볼 수 있도록 하는 어댑터(Adapter) 패턴 적용사례이기도 함
- 그러나 int, Integer 사이의 변환 과정 때문에 성능이 좋진 않음
- 정적 팩터리 메서드를 통해 반환되는 객체의 클래스가 숨겨진, 외부에서는 접근이 불가능한 익명 클래스 (anonymous class)라는 점에 주의
- int 배열을 Integer 객체의 리스트처럼 볼 수 있도록 하는 어댑터(Adapter) 패턴 적용사례이기도 함
- 추상 골격 구현 클래스란?
-
골격 구현 클래스의 이점
- 추상 클래스를 자료형 정의 수단으로 사용했을 때 만족해야 하는 심각한 제약사항들을 따르지 않아도 추상 클래스를 구현할 수 있도록 돕는 다는 것
- 골격 구현 클래스가 있다면 해당 클래스를 사용해 인터페이스를 구현하는 것이 가장 분명한 프로그래밍 방법임
- 골격 구현 클래스를 상속하도록 기존 클래스를 변경할 수 없다면, 인터페이스를 직접 구현해도 됨
- 추상 클래스를 자료형 정의 수단으로 사용했을 때 만족해야 하는 심각한 제약사항들을 따르지 않아도 추상 클래스를 구현할 수 있도록 돕는 다는 것
-
골격 구현 클래스의 예제 (Map.Entry 인터페이스에 대한 골격 구현 클래스)
- 인터페이스를 살펴보고 다른 메서드를 구현할 때 쓸 기본 메서드를 추림
- 이때 추린 메서드들은 추상 메서드로 선언하고, 나머지 메서드들은 실제로 구현해서 넣음
//골격 구현 public abstract class AbstractMapEntry<K, V> implements Map.Entry<K, V> { //기본(primitive) 메서드 public abstract K getKey(); public abstract V getValue(); //변경 가능 맵에 들어갈 Entry는 이 메서드를 재정의해야 한다. public V setValue(V value) { throw new UnsupportedOperationException(); } //Map.Entry.equlas의 일반 규약 구현 @Override public boolean equals(Object o) { if (o == this) return true; if (!( o instanceof Map.Entry)) return false; Map.Entry<?, ?> arg = (Map.Entry) o; return equals(getKey(), arg.getKey()) && equals(getValue(), arg.getValue()); } private static boolean equals(Object o1, Object o2) { return o1 == null ? o2 == null : o1.equals(o2); } //Map.Entry.hashCode의 일반 규약 구현 @Override public int hashCode() { return hashCode(getKey()) ^ hashCode(getValue()); } private static int hashCode(Object obj) { return obj == null ? 0 : obj.hashCode(); } }
- 골격 구현 클래스는 계승 용도로 설계하는 클래스이므로, 규칙 17의 모든 지침을 따라야 함
- 인터페이스를 살펴보고 다른 메서드를 구현할 때 쓸 기본 메서드를 추림
-
골격 구현의 작은 변종 가운데 하나인 간단 구현(simple implementation)
- 대표적인 예로 AbstractMap.SinpleEntry
public static class SimpleEntry<K,V> implements Entry<K,V>, java.io.Serializable{ private final K key; private V value; public SimpleEntry(K key, V value) { this.key = key; this.value = value; } public SimpleEntry(Entry<? extends K, ? extends V> entry) { this.key = entry.getKey(); this.value = entry.getValue(); } public K getKey() { return key; } public V getValue() { return value; } public V setValue(V value) { V oldValue = this.value; this.value = value; return oldValue; } public boolean equals(Object o) { if (!(o instanceof Map.Entry)) return false; Map.Entry<?,?> e = (Map.Entry<?,?>)o; return eq(key, e.getKey()) && eq(value, e.getValue()); } public int hashCode() { return (key == null ? 0 : key.hashCode()) ^ (value == null ? 0 : value.hashCode()); } public String toString() { return key + "=" + value; } }
- 간단 구현은 인터페이스를 구현한 클래스라는 점, 계승을 고려해 만들어진 클래스라는 점에서 골격 구현과 같지만 추상 클래스가 아니라는 점에서 다름
- 실제로 동작하는 구현체 가운데 가장 간단한 형태
- 대표적인 예로 AbstractMap.SinpleEntry
결론
- 인터페이스는 다양한 구현이 가능한 자료형을 정의하는 일반적으로 가장 좋은 방법이다.
- 인터페이스를 통해서 강력한 유연성을 얻을 수 있음
- 다만, 유연하고 강력한 API를 만드는 것보다 개선이 쉬운 API를 만드는 것이 중요한 경우는 예외
- 이런 경우에는 추상 클래스를 사용해야 하는데, 그 단점은 잘 이해하고 있어야 함
- 중요한 인터페이스를 API에 포함시키는 경우에는 골격 구현 클래스를 함께 제공하면 어떨지 심각하게 고려해봐야 함
- public 인터페이스는 극도로 주의해서 설계 해야 함
반응형
'프로그래밍 > EffectiveJava' 카테고리의 다른 글
(이펙티브 자바) 규칙7. 다 쓴 객체 참조를 해제하라 (0) | 2020.02.24 |
---|---|
(이펙티브 자바) 규칙22. 인터페이스는 타입을 정의하는 용도로만 사용하라 (0) | 2020.02.20 |
(이펙티브 자바) 규칙18. 상속보다는 컴포지션을 사용하라 (0) | 2020.02.13 |
(이펙티브 자바) 규칙 1. 생성자 대신 정적 팩터리 메서드를 고려하라 (0) | 2020.02.11 |
(이펙티브 자바) 규칙6. 불필요한 객체 생성을 피하라 (0) | 2020.02.10 |
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
- Total
- Today
- Yesterday
TAG
- intelij
- 이펙티브자바
- 자전거
- springboot
- effectivejava
- JavaFX 테이블뷰
- Java UI
- 일본 자전거 여행
- 텐트
- 자바
- TableView
- JavaFX
- 인텔리제이
- 이펙티브
- JavaFX Table View
- windows
- 일본 여행
- 배낭 여행
- 스프링부트
- java
- 방통대 과제물
- 자전거 여행
- 일본 배낭여행
- JavaFX 종료
- effective java
- 이펙티브 자바
- git
- JavaFX Window Close
- 배낭여행
- 일본여행
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 | 31 |
글 보관함