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

所以实际上它是两个不同类的两个不同对象。如果不比较它们的字节码,就很难断定它们做了同样的事情。另请注意,它们都捕获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));
}
于 2015-05-11T04:30:40.420 回答