为什么没有方法引用单身?

时间:2014-12-23 08:17:38

标签: java lambda java-8 conceptual

在Java中,以下代码在两个查询上都返回false。为什么?方法引用是单例是不是更简单?它肯定会使附加和分离听众变得更加简单。因为您需要为任何需要进行等效性检查的方法引用保持常量,所以您不能在每个必要的位置使用方法引用运算符。

public class Main {

    public Main() {
        // TODO Auto-generated constructor stub
    }

    public void doStuff() {

    }

    public static void main(String[] args) {
        Main main = new Main();
        Runnable thing1 = main::doStuff;
        Runnable thing2 = main::doStuff;
        System.out.println(thing1 == thing2); // false
        System.out.println(thing1.equals(thing2)); // false
    }

}

3 个答案:

答案 0 :(得分:16)

对于实例方法,我不认为对它们进行缓存是有意义的。你必须为每个实例缓存一个方法...这或者意味着与该方法相关联的类中的额外字段 - 每个公共方法一个,大概是因为这些方法可以从类外部引用 - 或者缓存在方法引用的 user ,在这种情况下,您需要某种每实例缓存。

我认为对缓存 static 方法的方法引用更有意义,因为它们将永远是相同的。但是,要缓存实际的Runnable,您需要为其定位的每种类型的缓存。例如:

public interface NotRunnable {
    void foo();
}

Runnable thing1 = Main::doStuff; // After making doStuff static
NotRunnable thing2 = Main::doStuff;

这里thing1thing2应该相等吗?如果是这样,编译器将如何知道要创建的类型?它可以在这里创建两个实例并单独缓存它们 - 并始终在使用点缓存而不是方法声明点。 (您的示例具有相同的类,声明方法并引用它,这是一个非常特殊的情况。您应该考虑更一般的情况,它们是不同的>)

JLS 允许缓存方法引用。来自section 15.13.3

  

接下来,分配并初始化具有以下属性的类的新实例,或者引用具有以下属性的类的现有实例。

...但即使对于静态方法,似乎javac目前也没有进行任何缓存。

答案 1 :(得分:4)

在这个简单的例子中,您可以按照您的建议通过适当创建额外的静态或实例字段来做,如果lambda引用对象则更复杂,但目的是内联这些实例,例如。

List<String> list = ....
list.stream().filter(s -> !s.isEmpty()).forEach(System.out::println);

应该同样有效(甚至不再创建对象)

for (String s : list)
    if(!s.isEmpty())
         System.out.println(s);

它可以通过内联流代码消除对象,并使用转义分析来消除首先创建对象的需要。

由于这个原因,很少关注实现equals(),hashCode()或toString(),通过反射来访问它们。 AFAIK,这是故意避免使用非预期的对象。

答案 2 :(得分:2)

制作方法单例将需要同步获取对它们的引用。这给这种简单的操作带来了很大的开销,并且结果不可预测。另一种解决方案是在类加载时为每个方法创建对象,但这会产生许多冗余对象,因为只有很少的方法需要引用。我认为同步是主要问题。