初探java SE8中Default Methods

    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

相關文章
相關標籤/搜索