1. 程式人生 > 實用技巧 >java8 default methods 預設方法的概念與程式碼解析

java8 default methods 預設方法的概念與程式碼解析

一、基本概念

Default methods enable you to add new functionality to the interfaces of your libraries and ensure binary compatibility with code written for older versions of those interfaces.
預設方法使您能夠新增新的功能到你現有庫的介面中,並確保與採用老版本介面編寫的程式碼的二進位制相容性。

什麼是預設方法(default methods)
即介面可以有實現方法,而且不需要實現類去實現其方法。只需在方法名前面加個default關鍵字即可,這些方法預設是


為什麼要有預設方法
為什麼要有這個特性?首先,之前的介面是個雙刃劍,好處是面向抽象而不是面向具體程式設計,缺陷是,當需要修改介面時候,需要修改全部實現該介面的類,目前的java 8之前的集合框架沒有foreach方法,通常能想到的解決辦法是在JDK裡給相關的介面新增新的方法及實現。然而,對於已經發布的版本,是沒法在給介面新增新方法的同時不影響已有的實現。所以引進的預設方法。他們的目的是為了解決介面的修改與現有的實現不相容的問題

二、java 8抽象類與介面的區別
相同點:
1.都是抽象型別;
2.都可以有實現方法(java8才可以)
3.都可以不需要實現類或者繼承者去實現所有方法

不同點
1.抽象類不可以多重繼承,介面可以(無論是多重型別繼承還是多重行為繼承);
2.抽象類和介面所反映出的設計理念不同。其實抽象類表示的是"is-a"關係,介面表示的是"like-a"關係;
3.介面中定義的變數預設是public static final 型,且必須給其初值,所以實現類中不能重新定義,也不能改變其值;抽象類中的變數預設是 friendly 型,其值可以在子類中重新定義,也可以重新賦值。

三、多重繼承的衝突說明
由於同一個方法可以從不同介面引入,自然而然的會有衝突的現象,預設方法判斷衝突的規則如下:
1.一個宣告在類裡面的方法優先於任何預設方法(classes always win)
2.否則,則會優先選取最具體的實現,比如下面的例子 B重寫了A的hello方法。

四、如何擴充套件或實現帶有預設方法的介面?

當前擴充套件一個預設方法的介面時,你可以採用以下三種方式:

1:讓擴充套件類繼承預設方法,根據不管是否存在預設方法

2:重新宣告預設方法,使其變為一個抽象方法(注意,擴充套件類的實現類必須實現此方法)

3:重新定義預設方法,覆蓋(override)父類的預設方法

五、預設方法樣例程式碼

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 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));
	}
}
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

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 year, int month, int day) {
		LocalDate dateToSet = LocalDate.of(year, month, day);
		LocalTime currentTime = LocalTime.from(dateAndTime);
		dateAndTime = LocalDateTime.of(dateToSet, currentTime);
	}

	public void setDateAndTime(int year, int month, int day, int hour,
			int minute, int second) {
		LocalDate dateToSet = LocalDate.of(year, month, day);
		LocalTime timeToSet = LocalTime.of(hour, minute, second);
		dateAndTime = LocalDateTime.of(dateToSet, timeToSet);
	}

	public LocalDateTime getLocalDateTime() {
		return dateAndTime;
	}

	public String toString() {
		return dateAndTime.toString();
	}

	public static void main(String... args) {
		TimeClient client = new SimpleTimeClient();
		// 顯示當前日期時間
		System.out.println(client.toString());
		// 設定日期
		client.setTime(11, 12, 22);
		System.out.println(client);
		// 設定時間
		client.setDate(2012, 11, 12);
		System.out.println(client);

		System.out.println("Time in Asia/Shanghai: "
				+ client.getZonedDateTime("Asia/Shanghai").toString());
	}
}

六、整合預設方法、靜態方法到已經存在的介面

預設方法使您能夠新增新的功能到已經存在的介面,確保與採用老版本這些介面編寫的程式碼的二進位制相容性。特別是,預設的方法使您能夠在已經存在的介面中新增使用lambda表示式作為引數的方法。下面的樣例程式碼說明通過預設方法和靜態方法,Comparator介面是如何提供豐富的功能的。

在java8中,Compartor介面提供了豐富的功能,提供了差不多近20個預設或靜態方法,在以前的版本中僅僅提供了compare(To1,To2)一個比較介面方法

下面的程式碼是有關撲克牌遊戲中的洗牌,針對牌排序,打散,發牌的部分原始碼

package defaultmethods;

//撲克牌介面類
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();
}
package defaultmethods;

import java.util.Comparator;
import java.util.List;
import java.util.Map;

//牌桌介面類
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;

}
package defaultmethods;

import java.util.Comparator;

//先根據rank,再根據suit進行比較
public class SortByRankThenSuit implements Comparator<Card> {
	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();
	}
}
package defaultmethods;

//撲克牌實現類
public class PlayingCard implements Card {

	private Card.Rank rank;
	private Card.Suit suit;

	public PlayingCard(Card.Rank rank, Card.Suit suit) {
		this.rank = rank;
		this.suit = suit;
	}

	public Card.Suit getSuit() {
		return suit;
	}

	public Card.Rank getRank() {
		return rank;
	}

	public boolean equals(Object obj) {
		if (obj instanceof Card) {
			if (((Card) obj).getRank() == this.rank
					&& ((Card) obj).getSuit() == this.suit) {
				return true;
			} else {
				return false;
			}
		} else {
			return false;
		}
	}

	public int hashCode() {
		return ((suit.value() - 1) * 13) + rank.value();
	}

	//實現比較介面
	public int compareTo(Card o) {
		return this.hashCode() - o.hashCode();
	}

	//過載toString
	public String toString() {
		return this.rank.text() + " of " + this.suit.text();
	}

	public static void main(String... args) {
		new PlayingCard(Rank.ACE, Suit.DIAMONDS);
		new PlayingCard(Rank.KING, Suit.SPADES);
	}
}
package defaultmethods;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

//牌桌實現類
public class StandardDeck implements Deck {

	//撲克牌列表
	private List<Card> entireDeck;

	public StandardDeck(List<Card> existingList) {
		this.entireDeck = existingList;
	}

	public StandardDeck() {
		this.entireDeck = new ArrayList<>();
		for (Card.Suit s : Card.Suit.values()) {
			for (Card.Rank r : Card.Rank.values()) {
				this.entireDeck.add(new PlayingCard(r, s));
			}
		}
	}

	public Deck deckFactory() {
		return new StandardDeck(new ArrayList<Card>());
	}

	public int size() {
		return entireDeck.size();
	}

	public List<Card> getCards() {
		return entireDeck;
	}

	public void addCard(Card card) {
		entireDeck.add(card);
	}

	public void addCards(List<Card> cards) {
		entireDeck.addAll(cards);
	}

	public void addDeck(Deck deck) {
		List<Card> listToAdd = deck.getCards();
		entireDeck.addAll(listToAdd);
	}

	public void sort() {
		Collections.sort(entireDeck);
	}

	public void sort(Comparator<Card> c) {
		Collections.sort(entireDeck, c);
	}

	public void shuffle() {
		Collections.shuffle(entireDeck);
	}

	//為每位玩家分牌
	public Map<Integer, Deck> deal(int players, int numberOfCards)
			throws IllegalArgumentException {
		int cardsDealt = players * numberOfCards;
		int sizeOfDeck = entireDeck.size();
		
		if (cardsDealt > sizeOfDeck) {
			throw new IllegalArgumentException("Number of players (" + players
					+ ") times number of cards to be dealt (" + numberOfCards
					+ ") is greater than the number of cards in the deck ("
					+ sizeOfDeck + ").");
		}
		//把牌分成幾份
		int slices=players+1;
		if(cardsDealt == sizeOfDeck)
			slices=players;

		//根據玩家的個數,每個玩家分到的撲克牌數進行分牌
		Map<Integer, List<Card>> dealtDeck = entireDeck.stream().collect(
				Collectors.groupingBy(card -> {
					int cardIndex = entireDeck.indexOf(card);
					if (cardIndex >= cardsDealt)
						return (players + 1);
					else
						return (cardIndex % players) + 1;
				}));
		System.out.println(dealtDeck);
		// Convert Map<Integer, List<Card>> to Map<Integer, Deck>
		Map<Integer, Deck> mapToReturn = new HashMap<>();

		for (int i = 1; i < (slices + 1); i++) {
			Deck currentDeck = deckFactory();
			currentDeck.addCards(dealtDeck.get(i));
			mapToReturn.put(i, currentDeck);
		}
		return mapToReturn;
	}

	public String deckToString() {
		return this.entireDeck.stream().map(Card::toString)
				.collect(Collectors.joining("\n"));
	}
	
	public String toString(){
		return deckToString();
	}

	public static void main(String... args) {
		System.out.println("Creating deck:");
		StandardDeck myDeck = new StandardDeck();
		
		myDeck.sort();
		System.out.println("Sorted deck");
		System.out.println(myDeck.deckToString());
		
		myDeck.shuffle();
		myDeck.sort(new SortByRankThenSuit());
		System.out.println("Sorted by rank, then by suit");
		System.out.println(myDeck.deckToString());
		
		myDeck.shuffle();
		myDeck.sort(Comparator.comparing(Card::getRank).thenComparing(
				Comparator.comparing(Card::getSuit)));
		System.out.println("Sorted by rank, then by suit "
				+ "with static and default methods");
		System.out.println(myDeck.deckToString());

		myDeck.sort(Comparator.comparing(Card::getRank).reversed()
				.thenComparing(Comparator.comparing(Card::getSuit).reversed()));
		System.out.println("Sorted by rank reversed, then by suit "
				+ "with static and default methods");
		System.out.println(myDeck.deckToString());
		
		myDeck.shuffle();
		myDeck.sort(
		    (firstCard, secondCard) ->
		        firstCard.getRank().value() - secondCard.getRank().value()
		); 
		System.out.println(myDeck.deckToString());
		
		myDeck.shuffle();
		myDeck.sort(Comparator.comparing(Card::getRank));
		System.out.println(myDeck.deckToString());
		
		Map<Integer, Deck> map=myDeck.deal(4, 11);
		for(Map.Entry<Integer, Deck> item:map.entrySet()){
			System.out.println(item.getKey());
			System.out.println(item.getValue());
			System.out.println("-------------------------------");
		}
	}
}

七、參考

http://docs.oracle.com/javase/8/docs/api/java/util/Comparator.html

http://docs.oracle.com/javase/tutorial/java/IandI/defaultmethods.html

http://docs.oracle.com/javase/tutorial/java/TOC.html

來源:站長資訊