default methods容许在添加新的功能到你库的接口上,确保老版本接口代码二进制兼容。
考虑下面接口
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();
}
下面SimpleTimeClient类实现以上接口
public class SimpleTimeClient implements TimeClient {
private LocalDateTime dateAndTime;
public SimpleTimeClient() {
dateAndTime = LocalDateTime.now();
}
@Override
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);
}
@Override
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);
}
@Override
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);
}
@Override
public LocalDateTime getLocalDateTime() {
return dateAndTime;
}
@Override
public String toString() {
return dateAndTime.toString();
}
public static void main(String[] args) {
SimpleTimeClient myTimeClient = new SimpleTimeClient();
System.out.println(myTimeClient.toString());
}
}
假如你须要在 TimeClient接口中添加新功能,如经过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();
ZonedDateTime getZonedDateTime(String zoneString);
}
修改 TimeClient接口,你也须要修改 SimpleTimeClient类并实现 getZonedDateTime方法。然而,你能够定义一个默认实现来取代将getZonedDateTime做为抽象
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 getZoneId(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(), getZoneId(zoneString));
}
}
在方法签名的开始处使用关键字default来指定接口中方法定义为默认方法。接口中声明的方法包括默认方法,都是隐式为public,因此你能够忽略public的修饰符。
使用这个接口,你不须要去修改实现类SimpleTimeClient,而且这个类(全部已实现TimeClient接口的类)已经定义了 getZonedDateTime方法,直接能够调用,如:
public static void main(String[] args) {
SimpleTimeClient myTimeClient = new SimpleTimeClient();
System.out.println("Current time: " + myTimeClient.toString());
System.out.println("Time in California: " +
myTimeClient.getZonedDateTime("Blah blah").toString());
}
扩展接口包含默认方法
a、更不用说默认方法,让你的扩展接口继承默认方法
b、从新声明默认方法,这使它抽象
c、从新定义默认方法,这使其覆盖
假如你扩展 TimeClient接口,以下
public interface AnotherTimeClient extends TimeClient {}
任何实现 AnotherTimeClient接口的类将有 TimeClient.getZonedDateTime()经过默认方法
假如你扩展 TimeClient接口,以下
public interface AbstractZoneTimeClient extends TimeClient {
public ZonedDateTime getZonedDateTime(String zoneString);
}
任何实现 AbstractZoneTimeClient接口的类将实现 getZonedDateTime方法,该方法是一个抽象方法,相似于接口中其余全部非默认方法同样。
假如你扩展 TimeClient接口,以下
public interface HandleInvalidTimeZoneClient extends TimeClient {
default public ZonedDateTime getZonedDateTime(String zoneString) {
try {
return ZonedDateTime.of(getLocalDateTime(),ZoneId.of(zoneString));
} catch (DateTimeException e) {
System.err.println("Invalid zone ID: " + zoneString +
"; using the default time zone instead.");
return ZonedDateTime.of(getLocalDateTime(),ZoneId.systemDefault());
}
}
}
任何实现 HandleInvalidTimeZoneClient接口的类,将使用该接口中的 HandleInvalidTimeZoneClient. getZonedDateTime(),而非 TimeClient.getZonedDateTime()的实现
Static Method
除了默认方法,你能够在接口中定义static method。这使你在你的库中组织辅助方法更容易,你能够保持静态方法在相同的接口中,而不是在单独的类中。下面定义一个根据时区标识符遍历ZoneId的静态方法:
static ZoneId getZoneId(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();
}
}
在接口中声明的全部静态方法,都隐式为public,因此能够忽略public修饰符
集成默认方法到已存在的库中
默认方法能够添加LE做为参数添加到已经存在接口,例如
public interface Card extends Comparable<Card> {
public enum Suit {
DIAMONDS(1, "Diamonds"),
CLUBS(2, "Clubs"),
HEARTS(3, "Hearts"),
SPADES(4, "Spades");
private final int value;
private final String text;
Suit(int value, String text) {
this.value = value;
this.text = text;
}
public int value() {return value;}
public String text() {return text;}
}
public enum Rank {
DEUCE (2 , "Two" ),
THREE (3 , "Three"),
FOUR (4 , "Four" ),
FIVE (5 , "Five" ),
SIX (6 , "Six" ),
SEVEN (7 , "Seven"),
EIGHT (8 , "Eight"),
NINE (9 , "Nine" ),
TEN (10, "Ten" ),
JACK (11, "Jack" ),
QUEEN (12, "Queen"),
KING (13, "King" ),
ACE (14, "Ace" );
private final int value;
private final String text;
Rank(int value, String text) {
this.value = value;
this.text = text;
}
public int value() {return value;}
public String text() {return text;}
}
public Card.Suit getSuit();
public Card.Rank getRank();
}
public interface Deck {
List<Card> getCards();
Deck deckFactory();
int size();
void addCard(Card card);
void addCards(List<Card> cards);
void addDeck(Deck deck);
void shuffle();
void sort();
void sort(Comparator<Card> c);
String deckToString();
Map<Integer, Deck> deal(int players, int numberOfCards)
throws IllegalArgumentException;
}
PlayingCard实现接口Card,StandardDeck实现接口Deck
public class StandardDeck implements Deck {
private List<Card> entireDeck;
//...
@Override
public void sort() {
Collections.sort(entireDeck);
}
}
Collections.sort排序List实例中元素类型实现了Comparable接口,PlayingCard实现Comparable.compareTo方法:
@Override
public int hashCode() {
return ((suit.value()-1)*13)+rank.value();
}
@Override
public int compareTo(Card o) {
return this.hashCode() - o.hashCode();
}
该compareTo方法引发StandardDeck.sort()排序cards首先经过suit,而后经过rank。
若是你想先经过rank,而后经过suit?你必须实现Comparator接口来指定一个新的排序条件,并使用方法sort(List<T> list, Comparator<? super T> c)。你能够经过下面方法定义在StandardDeck:
@Override
public void sort(Comparator<Card> c) {
Collections.sort(entireDeck, c);
}
使用该方法,你能够指定Collections.sort的排序规则,下面是实现Comparator接口一种方法
public class SortByRankThenSuit implements Comparator<Card> {
@Override
public int compare(Card firstCard, Card secondCard) {
int compVal =
firstCard.getRank().value() - secondCard.getRank().value();
if (compVal != 0)
return compVal;
else
return firstCard.getSuit().value() - secondCard.getSuit().value();
}
}
调用该条排序规则
StandardDeck myDeck = new StandardDeck();
myDeck.shuffle();
myDeck.sort(new SortByRankThenSuit());
然而,这种方法太繁琐了,若是你能指定你想要的排序规则那就更加好,假如你是开发Comparator接口,为了方便其余开发人员更容易的指定排序条件,你可能添加默认方法or静态方法到Comparator接口?
开始,假如你想根据rank进行排序,不考虑suit,你可以下调用StandardDeck.sort:
StandardDeck myDeck = new StandardDeck();
myDeck.shuffle();
myDeck.sort(
(firstCard, secondCard) ->
firstCard.getRank().value() - secondCard.getRank().value()
);
由于Comparator接口是一个函数接口,你可使用LE做为参数为sort方法。
若是你建立一个Comparator实例来比较任何能返回数值类型的对象从方法如getValue 或hashCode。Comparator接口使用static方法comparing已经加强这方面的能力:
myDeck.sort(Comparator.comparing((card) -> card.getRank()));
在这个例子中,你可使用方法引用代替:
myDeck.sort(Comparator.comparing(Card::getRank()));
假如,你使用LE指定以下排序
StandardDeck myDeck = new StandardDeck();
myDeck.shuffle();
myDeck.sort(
(firstCard, secondCard) -> {
int compare =
firstCard.getRank().value() - secondCard.getRank().value();
if (compare != 0)
return compare;
else
return firstCard.getSuit().value() - secondCard.getSuit().value();
}
);
Comparator接口使用默认方法thencomparing已经加强这方面的能力
myDeck.sort(
Comparator
.comparing(Card::getRank)
.thenComparing(Comparator.comparing(Card::getSuit)));
ide