初探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

相关文章
相关标签/搜索