blog

Java8 デフォルト・メソッド デフォルト・メソッドの概念とコード解析

Java 8では、デフォルト・メソッド(仮想拡張メソッド)が導入され、インターフェイスを実装するすべてのクラスに新しいメソッドの実装を強制することなく、インターフェイスが後から新しいメソッドを追加でき...

Jun 17, 2014 · 22 min. read
シェア

I. 基本概念

デフォルト・メソッドを使えば、ライブラリのインターフェイスに新しい機能を追加したり、古いバージョンのインターフェイス用に書かれたコードとのバイナリの互換性を確保したりすることができます。バイナリの互換性を確保することができます。

デフォルトの方法では、既存のライブラリのインターフェイスに新しい機能を追加することができ、古いバージョンのインターフェイスを使用して書かれたコードとのバイナリ互換性が保証されます。

デフォルトの方法とは

つまり、インターフェイスは実装メソッドを持つことができ、実装クラスがそのメソッドを実装する必要はありません。メソッド名の前に default キーワードを追加するだけで、これらのメソッドはデフォルトで

デフォルトのメソッドを持つ理由

なぜこの機能なのでしょうか?まず第一に、インターフェイスは諸刃の剣です、利点は具体的なプログラミングではなく、抽象的なプログラミングに向いています、欠点は、インターフェイスを変更する必要があるとき、インターフェイスを実装するすべてのクラスを変更する必要があります、現在のJava 8コレクションフレームワークはforeachメソッドを持っていません、通常、解決策を考えることができるJDKで関連するインターフェイスと実装に新しいメソッドを追加することです。しかし、リリースされたバージョンでは、既存の実装に影響を与えることなくインターフェイスに新しいメソッドを追加することはできません。そこで、デフォルト・メソッドが導入されました。その目的は、インターフェイスの変更と既存の実装との間の非互換性の問題を解決することです。

第二に、java 8の抽象クラスとインターフェースの違いです。

同じ点です:

1.は抽象型。

2.すべての実現方法

3. クラスを実装したり、後継者がすべてのメソッドを実装する必要はありません。

違い目

1.抽象クラスは多重継承できませんが、インターフェースは可能です。

2.抽象クラスとインターフェースは異なる設計概念を反映しています。実際、抽象クラスは "is-a "の関係を表し、インターフェースは "like-a "の関係を表します。

3.インターフェイスで定義された変数は、デフォルトではpublic static final型であり、その初期値を与えなければならないので、クラスの実装は再定義することができず、その値を変更することはできません;抽象クラスの変数は、デフォルトではfriendly型であり、その値はサブクラスで再定義することができ、また、値を再割り当てすることができます。

III.数次相続の矛盾の説明

同じメソッドが異なるインターフェイスから導入される可能性があるため、競合が発生するのは当然であり、競合を判断するためのデフォルトのメソッドルールは以下のとおりです:

1.クラス内部で宣言されたメソッドは、デフォルト・メソッドよりも優先されます。

2.そうでなければ、次の例のように、BがAのhelloメソッドをオーバーライドするように、***本体の実装が優先されます。

IV.デフォルトメソッドを持つインタフェースを拡張または実装する方法

現在、デフォルトメソッドでインターフェイスを拡張する場合、3つの方法のうちの1つで行うことができます:

1: デフォルトメソッドが存在するかどうかに基づいて、拡張クラスにデフォルトメソッドを継承させます。

2: デフォルトメソッドを再宣言し、抽象メソッドにします。

3: デフォルト・メソッドを再定義して、親クラスのデフォルト・メソッドをオーバーライドします。

V. デフォルト・メソッドのサンプル・コード

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/Tokyo: " 
                + client.getZonedDateTime("Asia/Tokyo").toString());  
    }  
}  

VI. 既存のインターフェースへのデフォルトメソッドと静的メソッドの統合

デフォルトのメソッドを使用すると、既に存在するインターフェースに新しい機能を追加することができ、これらのインターフェースの古いバージョンを使用して書かれたコードとのバイナリ互換性が保証されます。特に、既定メソッドを使用すると、ラムダ式をパラメーターとして使用するメソッドを既存のインターフェースに追加できます。次のサンプル・コードは、Comparator インターフェースがデフォルト・メソッドと静的メソッドを通じてどのように豊富な機能を提供するかを示しています。

java8では、Compartorインターフェイスは豊富な機能を提供し、ほぼ20のデフォルトまたは静的メソッドを提供します。

以下のコードは、ポーカーゲームでカードをシャッフル、ソート、ブレイク、ディールするためのソースコードの一部です。

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;  
 
//ランクで比較し、次にスートで比較する。  
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();  
    }  
 
    //compareインターフェイスの実装  
    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("
"));  
    }  
      
    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("-------------------------------");  
        }  
    }  
}  

Read next

Java 8の新機能を探る(5):アノテーションを繰り返す

JEP120はあまり内容がなく、コードの可読性を向上させるだけの小さな機能です。今回、java 8ではアノテーションに2つの改良が加えられたので、アノテーションは以前よりもよく使われるようになると思います。

Jun 15, 2014 · 2 min read