java中功能接口实例的等价性

Bin*_*obe 8 java equality immutability java-8 functional-interface

我不确定如何确定功能接口的相等/不变性.我想在java 8中使用这个语法糖时可能无法保证平等,如果你有任何提示,请告诉我任何提示.

我为我的问题制作了一个简短的代码片段.

public interface Element {
    void doSomething(int a);
}
Run Code Online (Sandbox Code Playgroud)

我试图以功能方式添加此接口的实例

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));
    }

}
Run Code Online (Sandbox Code Playgroud)

它打印为true,这意味着没有任何相等检查,并且我添加它后也无法从Set中删除任何实例.

如果我使用功能接口作为参数,有什么方法可以以某种方式比较这些实例?

将提供任何帮助,谢谢!

Tag*_*eev 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));
}
Run Code Online (Sandbox Code Playgroud)

这样就返回 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);
        }
    }));
}
Run Code Online (Sandbox Code Playgroud)

所以每次它都是不同的对象,尽管它可能看起来相同。对于每个遇到的方法引用,都会在运行时创建单独的匿名类:

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

System.out.println(e1.getClass());
System.out.println(e2.getClass());
Run Code Online (Sandbox Code Playgroud)

输出将是这样的:

class FunctionSet$$Lambda$1/918221580
class FunctionSet$$Lambda$2/1554547125
Run Code Online (Sandbox Code Playgroud)

所以实际上它是两个不同类的两个不同对象。如果不比较它们的字节码,就很难断定它们做了同样的事情。另请注意,它们都捕获functionSet变量,因此还应确保它在两个方法引用之间没有更改。

我能想到的唯一解决方法是将所有方法引用声明为代码中的常量,然后引用它们,而不是直接使用方法引用:

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));
}
Run Code Online (Sandbox Code Playgroud)