为什么Java8和Scala2.12 lambda缓存有区别?

Ken*_*ida 7 lambda jvm scala invokedynamic java-8

Java代码

package lambda_cache_example_java;

interface Semigroup1<A> {
  public A append(A a1, A a2);
}

interface Semigroup2<A> {
  public A append(A a1, A a2);

  public interface Foo{}
  public class Bar{}
}

class Main {
  static Semigroup1<Integer> intSemigroup1() {
    return (a1, a2) -> a1 + a2;
  }

  static Semigroup2<Integer> intSemigroup2() {
    return (a1, a2) -> a1 + a2;
  }

  public static void main(String[] args) {
    Semigroup1<Integer> x1 = intSemigroup1();
    Semigroup1<Integer> x2 = intSemigroup1();
    System.out.println(x1);
    System.out.println(x2);
    System.out.println(x1 == x2); // same instance

    Semigroup2<Integer> y1 = intSemigroup2();
    Semigroup2<Integer> y2 = intSemigroup2();
    System.out.println(y1);
    System.out.println(y2);
    System.out.println(y1 == y2); // same instance as well
  }
}
Run Code Online (Sandbox Code Playgroud)

Scala代码(版本2.12.0)

package lambda_cache_example_scala

trait Semigroup1[A] {
  def append(a1: A, a2: A): A
}

trait Semigroup2[A] {
  def append(a1: A, a2: A): A

  trait Foo
}

object Main {
  def intSemigroup1(): Semigroup1[Int] =
    (a1, a2) => a1 + a2

  def intSemigroup2(): Semigroup2[Int] =
    (a1, a2) => a1 + a2

  def main(args: Array[String]): Unit = {
    val x1 = intSemigroup1()
    val x2 = intSemigroup1()
    println(x1)
    println(x2)
    println(x1 eq x2) // same instance

    val y1 = intSemigroup2()
    val y2 = intSemigroup2()
    println(y1)
    println(y2)
    println(y1 eq y2) // not same
  }
}
Run Code Online (Sandbox Code Playgroud)

结果

$ sbt "runMain lambda_cache_example_java.Main" "runMain lambda_cache_example_scala.Main"
[info] Running lambda_cache_example_java.Main 
lambda_cache_example_java.Main$$Lambda$9/1908283686@44939bb7
lambda_cache_example_java.Main$$Lambda$9/1908283686@44939bb7
true
lambda_cache_example_java.Main$$Lambda$10/2119574930@7f206457
lambda_cache_example_java.Main$$Lambda$10/2119574930@7f206457
true
[success] Total time: 0 s, completed 2016/11/24 15:09:56
[info] Running lambda_cache_example_scala.Main 
lambda_cache_example_scala.Main$$$Lambda$11/2085010450@7b408c6e
lambda_cache_example_scala.Main$$$Lambda$11/2085010450@7b408c6e
true
lambda_cache_example_scala.Main$$anonfun$intSemigroup2$2@c5329e5
lambda_cache_example_scala.Main$$anonfun$intSemigroup2$2@752d3cd9
false
[success] Total time: 0 s, completed 2016/11/24 15:09:57
Run Code Online (Sandbox Code Playgroud)

Ale*_*lec 3

Scala 具有路径依赖类型Foo尽管从您的示例中并不明显,但可以构造嵌套特征,其中一个实例内的特征与另一个实例中的Semigroup2特征根本不兼容。这篇文章这个答案似乎是对路径依赖类型的很好的解释。FooSemigroup2

这意味着 的实例Semigroup2也是由其内部特征定义的,因此在引用其方法之一时必须进行闭包。由于每次我们尝试引用该方法时都会动态重新创建该闭包,因此匿名函数不同也就不足为奇了。

在Java中,情况并非如此。Semigroup2<A>.Foo是一种类型(与 Scala 不同,在 Scala 中您需要的实例Semigroup[A]来标识类型Foo)。

  • @srborlongan:这是一个实现细节,而不是一个概念。原则上,如果 JVM 能够证明它们将表现出相同的行为,则允许将这些实例合并为一个。必须强调的是,在 Java 中,为 lambda 表达式创建的实例的标识是有意未指定的,并且不得与形式逻辑混淆。例如 `Function.&lt;String&gt;identity()==Function.&lt;Integer&gt;identity()` 将因类型不兼容而被编译器拒绝,尽管对象是相同的,如通过 `Function.&lt;String&gt;identity( 证明的那样) )==(Object)Function.&lt;Integer&gt;identity()`。 (2认同)