-
아이템 20. 추상 클래스보다는 인터페이스를 우선하라TL;DR
자바의 다중 구현 메커니즘자바가 제공하는 다중 구현 메커니즘은 인터페이스와 추상 클래스, 이렇게 두가지다.
자바 8부터 인터페이스도 디폴트 메서드(default method)를 제공할 수 있어 이제는 두 메커니즘 모두 인스턴스 메서드를 구현 형태로 제공할 수 있다.
인터페이스와 추상 클래스의 차이점타입 제약 문제!!
인터페이스로 구현하면 이점기존 클래스에도 손쉽게 새로운 인터페이스를 구현해넣을 수 있다.
인터페이스는 믹스인(mixin) 정의에 안성맞춤이다.
인터페이스로는 계층구조가 없는 타입 프레임워크를 만들 수 있다.
// 가수 인터페이스
public interface Singer {
AudioClip sing(Song s);
}
// 작곡 인터페이스
public interface Songwriter {
Song compose(int chartPosition);
}
// 계층구조 없이 설계할 수 있다.
// Singer 인터페이스와 Songwriter 인터페이스를 모두 구현한 SingerSongwriter 인터페이스 정의
// 위에 두 인터페이스 메서드 뿐만 아니라 새로운 메서드까지 추가한 제 3의 인터페이스 정의 가능
public interface SingerSongwriter extends Singer, Songwriter {
AudioClip strum();
void actSeneitive();
}
public abstract class Singer {
abstract AudioClip sing(Song s);
}
public abstract class SongWriter {
abstract Song compose(int chartPosition);
}
public abstract class SingerSongWriter {
abstract AudioClip strum();
abstract void actSensitive();
abstract Song Compose(int chartPosition);
abstract AudioClip sing(String s);
}
인터페이스의 디폴트 메서드(default method)
디폴트 메서드의 제약
인터페이스와 추상 골격 구현(skeletal implementation) 클래스
List 인터페이스와 AbstractCollection 추상 클래스를 확장하여 AbstractInterface 골격 구현 클래스 생성 public abstract class AbstractList<E> extends AbstractCollection<E> implements List<E> {
...
}
AbstractList 예시
Map.Entry 예시
public abstract class AbstractMapEntry<K, V> implements Map.Entry<K, V> {
//변경 가능한 엔트리는 이 메서드를 반드시 재정의해야 한다.
@Override public V setValue(V value) {
throw new UnsupportedOperationException();
}
//Map.Entry.equals의 일반 규약을 구현한다.
@Override public boolean equals(Object o) {
if (0 == this)
return true;
if (!(o instanceof Map.Entry))
return false;
Map.Entry<?, ?> e = (Map.Entry) o;
return Objects.equals(e.getKey(), getKey()) && Objects.equals(e.getValue(), getValue());
}
// Map.Entry.hashCode의 일반 규약을 구현한다.
@Override public int hashCode() {
return Objects.hashCode(getKey())^ Objects.hashCode(getValue());
}
@Overrride public String toString() {
return getKey() + "=" + getValue();
}
} ✅ Map.Entry 인터페이스나 그 하위 인터페이스로는 이 골격 구현을 제공할 수 없다.
시뮬레이트한 다중 상속
골격 구현과 시뮬레이트한 다중 상속 예시 코드골격 구현 예시 코드 (템플릿 메서드 패턴)// 인터페이스
public interface Phone {
void booting();
void greeting();
void shutdown();
void process();
}
// 추상 골격 구현 클래스 (보통 Abstract[interface 이름] 네이밍을 사용)
public abstract class AbstractPhone implements Phone {
//같은 동작을 하는 메서드(기반 메서드)를 정의
@Override
public void booting() {
System.out.println("booting ...");
}
@Override
public void shutdown() {
System.out.println("shut down ...");
}
@Override
public void process() {
booting();
greeting();
shutdown();
}
}
public class IPhone extends AbstractPhone {
// 기반 메서드가 아닌 메서드 구현
@Override
public void greeting() {
System.out.println("I am iPhone");
}
}
public class GalaxyPhone extends AbstractPhone {
@Override
public void greeting() {
System.out.println("I am galaxy phone");
}
} 시뮬레이트한 다중 상속(simulated multiple inheritance)
public class PhoneManufacturer {
public void printManufacturer() {
System.out.println("Made by Apple");
}
} // 골격 구현을 확장한 클래스 정의
public class InnerAbstractPhone extends AbstractPhone {
@Override
public void greeting() {
System.out.println("I am iPhone");
}
} // 시뮬레이트한 다중 상속
public class IPhone extends PhoneManufacturer implements Phone{
// 골격 구현을 확장한 클래스를 private 내부 클래스로 정의 (합성, Composition)
private final InnerAbstractPhone innerAbstractPhone = new InnerAbstractPhone();
// 메서드 호출을 내부 클래스의 인스턴스에 전달.
@Override
public void booting() {
innerAbstractPhone.booting();
}
@Override
public void greeting() {
innerAbstractPhone.greeting();
}
@Override
public void shutdown() {
innerAbstractPhone.shutdown();
}
@Override
public void process() {
printManufacturer();
innerAbstractPhone.process();
}
} 단순 구현
|
Beta Was this translation helpful? Give feedback.
Replies: 2 comments 2 replies
-
추상클래스보단 인터페이스를 우선시 하는 이유에 대해 알 수 있게된 좋은 정리 감사합니다. ADT(Abstract Data Type, 추상 데이터 타입) : 세부 사항에서 벗어나 추상적으로 정의한 데이터 타입.저는 처음 data type (자료형 이라고 부르는)을 접했을 때 단순히 자바에서는 크게 2가지 기본 데이터 타입(int, boolean, 등등), 참조형 데이터 타입이 있다 정도로 이해하고 넘어갔습니다. 하지만 객체지향의 사실과 오해라는 책에서 추상화를 설명하는 부분에서
단 객체는 데이터는 아니다 객체는 행동이 중요한 것이고 상태는 행동의 결과로 초래된 부수효과를 쉽게 표현하기 위해 도입한 추상적인 개념일 뿐 (객체와 관련된 내용은 깊게 이야기 하진 않겠습니다.)
주의할 점은 클래스와 타입은 동일한 것이 아님 타입은 객체를 분류하기 위해 사용하는 개념 반면 클래스는 단지 타입을 구현할 수 있는 여러 구현 메커니즘 중 하나일 뿐 이렇듯 클래스를 타입을 구현하는 방법 중 하나이고 다시 본론으로 돌아오면 자바의 인터페이스가 ADT에 가장 잘 대응 되는 개념이라는 표현이 객체지향의 사실과 오해를 읽고나서 더 이해가 되기 시작했습니다. 세부 사항에서 벗어나 추상적으로 정의한 데이터 타입인 ADT는 매우 다양한 표현 방법이 있습니다. 대표적으로 자료구조인 List의 ADT를 표현해보면 ADT '리스트'
이 것을 자바의 인터페이스로 표현해 보겠습니다. public interface ListInterface {
void add (int i, Object x);
Object get(int i);
void remove(int i);
} 자바의 인터페이스 코드로 위 리스트 자료구조에 대한 ADT 서술을 잘 표현하고 있습니다.
결과적으로
즉 자바의 인터페이스는 추상화를 하는 이유를 결과로 내놓고 있고 ADT에 가장 잘 대응되는 개념인 이유도 알게 되었습니다. reference
@coalong 님께서 작성하신 주제와 조금 벗어난 내용을 왜 comment에 남겼을까? 라는 생각을 하실수도 있지만 정리하신다고 고생많으셨습니다. 감사합니다. |
Beta Was this translation helpful? Give feedback.
-
잘 읽었습니다 😄 해당 아이템 장을 읽으면서 생각해볼 만한 점을 나열해 보면 다음과 같습니다.
|
Beta Was this translation helpful? Give feedback.
추상클래스보단 인터페이스를 우선시 하는 이유에 대해 알 수 있게된 좋은 정리 감사합니다.
해당 글과 무관할 수 있으나
저는
자바의 인터페이스가 ADT에 가장 잘 대응 되는 개념
이라는 표현이 가장 와 닿았습니다.ADT(Abstract Data Type, 추상 데이터 타입) : 세부 사항에서 벗어나 추상적으로 정의한 데이터 타입.
저는 처음 data type (자료형 이라고 부르는)을 접했을 때 단순히 자바에서는 크게 2가지 기본 데이터 타입(int, boolean, 등등), 참조형 데이터 타입이 있다 정도로 이해하고 넘어갔습니다.
하지만 객체지향의 사실과 오해라는 책에서 추상화를 설명하는 부분에서
추상화 : 현실에서 출발하되 불필요한 부분을 도려내가면서 사물의 놀라운 본질을 드러나게 하는 과정 --> 불필요한 부분을 무시함으로써 현실에 존재하는 복잡성을 극복
데이터 타입 : 메모리 안에 저장된 데이터의 종류를 분류하는 데 사용하는 메모리 집합에 관한 메타데이터로 추상화 한 결과 --> 컴퓨터의 메모리에서는 0과 1만 존재 하여 타입이라는 질서가 존재하지 않는데 데이터의 종류에 맞게 추상화 한 결과
객체 : 객체지향 프로그램에서 객체를 일종의 데이터 처럼 사용하는데 객체를 타입에 따라 분류하고 그 타입에 이름을 붙이는 것은 결국 프로그램에서 사용할 데이터 타입을 선언하는 것과 같음 --> 객체 또한 추상화의 결과
단…