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