import java.time.DateTimeException;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
public interface TimeClient {
void setTime(int hour, int minute, int second);
void setDate(int day, int month, int year);
void setDateAndTime(int day, int month, int year, int hour, int minute, int second);
LocalDateTime getLocalDateTime();
}
위와 같은 TimeClient인터페이스도 언젠가 시간이 지나다보면 변경을 하고싶어지는 경우가 있을 수 있습니다. 예를 들어 새로운 메서드를 추가하고싶지만 인터페이스에 메서드를 추가하게되면 TimeClient의 구현체들은 모두 TimeClient의 인터페이스에 새로 추가한 메서드를 모두 구현해줘야하는데 현실적으로 불가능합니다. 그래서 자바8 이후로 인터페이스에 default메서드를 구현하면 이러한 문제를 방지할 수 있습니다.
static ZoneId getZonedId(String zoneString) {
try {
return ZoneId.of(zoneString);
} catch (DateTimeException e) {
System.err.println("Invalid time zone: " + zoneString + "; using default time zone instead.");
return ZoneId.systemDefault();
}
}
default ZonedDateTime getZonedDateTime(String zoneString) {
return ZonedDateTime.of(getLocalDateTime(), getZonedId(zoneString));
}
default메서드를 추가해도 TimeClient의 구현체들은 이 default메서드를 재정의하지 않아도 됩니다.
package me.whiteship.chapter04.item20.defaultmethod;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
public class SimpleTimeClient implements TimeClient {
private LocalDateTime dateAndTime;
public SimpleTimeClient() {
dateAndTime = LocalDateTime.now();
}
public void setTime(int hour, int minute, int second) {
LocalDate currentDate = LocalDate.from(dateAndTime);
LocalTime timeToSet = LocalTime.of(hour, minute, second);
dateAndTime = LocalDateTime.of(currentDate, timeToSet);
}
public void setDate(int day, int month, int year) {
LocalDate dateToSet = LocalDate.of(day, month, year);
LocalTime currentTime = LocalTime.from(dateAndTime);
dateAndTime = LocalDateTime.of(dateToSet, currentTime);
}
public void setDateAndTime(int day, int month, int year,
int hour, int minute, int second) {
LocalDate dateToSet = LocalDate.of(day, month, year);
LocalTime timeToSet = LocalTime.of(hour, minute, second);
dateAndTime = LocalDateTime.of(dateToSet, timeToSet);
}
public LocalDateTime getLocalDateTime() {
return dateAndTime;
}
public String toString() {
return dateAndTime.toString();
}
}
예를들어 위와같이 TimeClient인터페이스를 구현하는 SimpleTimeClient클래스가 추가적으로 다른 인터페이스를 추가해서 구현을 할 수 있다.
public class SimpleTimeClient implements TimeClient, AutoCliseable {
// 코드 생략
}
타입간에 계층구조가 명확하지 않은 경우가 있는데 그때 여러 인터페이스를 조합해서 하나의 타입으로 만들 수 있다.
public interface Singer {
AudioClip sing(Song song);
}
public interface Songwriter {
Song compose(int shartPosition);
}
public interface SingerSongwriter extends Singer, Songwriter{
AudioClip strum();
void actSensitive();
}
가수와 작사의 관계는 계층관계가 아닙니다. 하지만 가수와 작사를 합쳐서 싱어송라이터라는 새로운 타입을 선언할 수 있습니다.
import java.util.Collection;
import java.util.Iterator;
import java.util.Set;
// 코드 18-3 재사용할 수 있는 전달 클래스 (118쪽)
public class ForwardingSet<E> implements Set<E> {
private final Set<E> s;
public ForwardingSet(Set<E> s) { this.s = s; }
public void clear() { s.clear(); }
public boolean contains(Object o) { return s.contains(o); }
public boolean isEmpty() { return s.isEmpty(); }
public int size() { return s.size(); }
public Iterator<E> iterator() { return s.iterator(); }
public boolean add(E e) { return s.add(e); }
public boolean remove(Object o) { return s.remove(o); }
public boolean containsAll(Collection<?> c)
{ return s.containsAll(c); }
public boolean addAll(Collection<? extends E> c)
{ return s.addAll(c); }
public boolean removeAll(Collection<?> c)
{ return s.removeAll(c); }
public boolean retainAll(Collection<?> c)
{ return s.retainAll(c); }
public Object[] toArray() { return s.toArray(); }
public <T> T[] toArray(T[] a) { return s.toArray(a); }
@Override public boolean equals(Object o)
{ return s.equals(o); }
@Override public int hashCode() { return s.hashCode(); }
@Override public String toString() { return s.toString(); }
}
아이템 18에서 살펴본것과 같이 컴포지션을 사용하여 Set인터페이스를 구현하는 ForwardingSet은 캡슐화가 되어있기때문에 안전하게 사용가능합니다.
import java.time.DateTimeException;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
public interface TimeClient {
void setTime(int hour, int minute, int second);
void setDate(int day, int month, int year);
void setDateAndTime(int day, int month, int year, int hour, int minute, int second);
LocalDateTime getLocalDateTime();
static ZoneId getZonedId(String zoneString) {
try {
return ZoneId.of(zoneString);
} catch (DateTimeException e) {
System.err.println("Invalid time zone: " + zoneString + "; using default time zone instead.");
return ZoneId.systemDefault();
}
}
default ZonedDateTime getZonedDateTime(String zoneString) {
return ZonedDateTime.of(getLocalDateTime(), getZonedId(zoneString));
}
}