【问题标题】:Why is there a difference between Java8 and Scala2.12 lambda cache?为什么 Java8 和 Scala2.12 lambda 缓存有区别?
【发布时间】:2016-11-24 06:36:28
【问题描述】:

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

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

结果

$ 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

【问题讨论】:

    标签: scala lambda java-8 jvm invokedynamic


    【解决方案1】:

    Scala 有依赖路径的类型。尽管从您的示例中并不明显,但可以构造嵌套特征,其中一个 Semigroup2 实例中的特征 FooSemigroup2 的另一个实例中的 Foo 完全不兼容。 This postthis answer 似乎很好地解释了路径相关类型。

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

    在 Java 中,情况并非如此。 Semigroup2&lt;A&gt;.Foo 是一个类型(不像在 Scala 中,您需要一个 Semigroup[A]instance 来识别类型 Foo)。

    【讨论】:

    • 最接近 Scala 行为的 Java 等效项是 Main::intSemigroup2 的 lambda 捕获 Semigroup2.Foo 实例,如 (a1 + a2) -> { foo.hashCode();返回 a1 + a2; })。这将强制 Main::intSemigroup2 总是返回一个新的 lambda,因为 Java 不能保证捕获的 Foo 值是可内联的。
    • @srborlongan 我想是这样 - 我认为这个问题更多的是“为什么 Scala 不返回相同的函数”而不是“为什么 Java 不返回不同的函数。好点。跨度>
    • 最后一段需要更正。在Java中,嵌套接口总是隐含的static,所以Semigroup2&lt;A&gt;.Foo不是一个类型,只有Semigroup2.Foo,一个单一的接口。相比之下,Semigroup2&lt;A&gt;.Bar 类型 是通用的,所以 概念上Semigroup2&lt;String&gt;.BarSemigroup2&lt;Integer&gt;.Bar 是不同的类型,但由于 类型擦除,只有一个Class代表Semigroup2.Bar
    • @srborlongan:这是一个实现细节,而不是一个概念。原则上,如果 JVM 可以证明它们会表现出相同的行为,则允许 JVM 将这些实例合二为一。必须强调的是,在 Java 中,为 lambda 表达式创建的实例的身份是有意未指定的,并且不能与形式逻辑混淆。例如。 Function.&lt;String&gt;identity()==Function.&lt;Integer&gt;identity() 将由于类型不兼容而被编译器拒绝,尽管对象是相同的,这可以通过 Function.&lt;String&gt;identity()==(Object)Function.&lt;Integer&gt;identity() 证明。
    猜你喜欢
    • 2011-04-11
    • 2021-04-28
    • 2017-07-15
    • 2015-12-16
    • 2011-09-22
    • 2015-03-11
    • 1970-01-01
    • 2015-01-08
    • 2017-11-24
    相关资源
    最近更新 更多