8

機能インターフェースの同等性/不変性についてどのように確信できるかわかりません。Java 8 でこの構文糖衣を使用すると、等価性を保証する方法がないのではないかと思います。何かヒントがあれば教えてください。

質問の短いコード スニペットを作成しました。

public interface Element {
    void doSomething(int a);
}

そして、機能的な方法でこのインターフェースのインスタンスを追加しようとしました

public class FunctionSet {

    public void doubleUp(int a) {
        System.out.println(a*2);
    }

    public void square(int a) {
        System.out.println(a*a);
    }

    public static void main(String[] args) {
        HashSet<Element> set = new HashSet<>();
        FunctionSet functionSet = new FunctionSet();

        set.add(functionSet::doubleUp);
        set.add(functionSet::square);

        System.out.println(set.add(functionSet::doubleUp));
    }

}

true が出力されます。これは、等価チェックがなかったことを意味し、一度追加すると Set からインスタンスを削除することもできません。

関数型インターフェースを引数として使用する場合、それらのインスタンスを何らかの方法で比較できる方法はありますか?

事前に感謝します。

4

1 に答える 1

3

メソッド参照を変数に格納できます。

public static void main(String[] args) {
    HashSet<Element> set = new HashSet<>();
    FunctionSet functionSet = new FunctionSet();

    Element fn = functionSet::doubleUp;
    set.add(fn);
    set.add(functionSet::square);

    System.out.println(set.add(fn));
}

このように false を返します。

異なるコードの場所に同じ labmda またはメソッド参照を作成する場合、両方の位置に新しい匿名クラスを作成する場合とほぼ同じです。

public static void main(String[] args) {
    HashSet<Element> set = new HashSet<>();
    FunctionSet functionSet = new FunctionSet();

    set.add(new Element() {
        @Override
        public void doSomething(int a) {
            functionSet.doubleUp(a);
        }
    });
    set.add(new Element() {
        @Override
        public void doSomething(int a) {
            functionSet.square(a);
        }
    });

    System.out.println(set.add(new Element() {
        @Override
        public void doSomething(int a) {
            functionSet.doubleUp(a);
        }
    }));
}

そのため、同じように見える場合でも、毎回異なるオブジェクトになります。遭遇したすべてのメソッド参照に対して、個別の匿名クラスが実行時に作成されます。

Element e1 = functionSet::doubleUp;
Element e2 = functionSet::doubleUp;

System.out.println(e1.getClass());
System.out.println(e2.getClass());

出力は次のようになります。

class FunctionSet$$Lambda$1/918221580
class FunctionSet$$Lambda$2/1554547125

したがって、実際には、2 つの異なるクラスの 2 つの異なるオブジェクトです。バイトコードを比較せずに、同じことをしていると結論付けるのは非常に困難です。また、どちらも変数をキャプチャするfunctionSetため、2 つのメソッド参照間で変数が変更されていないことも確認する必要があります。

私が考えることができる唯一の回避策は、メソッド参照を直接使用する代わりに、コード内ですべてのメソッド参照を定数として宣言し、後でそれらを参照することです。

public static final Element FN_DOUBLE_UP = new FunctionSet()::doubleUp; 
public static final Element FN_SQUARE = new FunctionSet()::square; 

public static void main(String[] args) {
    HashSet<Element> set = new HashSet<>();

    set.add(FN_DOUBLE_UP);
    set.add(FN_SQUARE);

    System.out.println(set.add(FN_DOUBLE_UP));
}
于 2015-05-11T04:30:40.420 に答える