为什么Scala 2.11编译器重命名我的私有方法?

时间:2018-02-16 16:33:47

标签: scala

当我编译以下代码以观察编译的方法名称不同时,我感到非常惊讶:

package com.toto
class A {
    def foo1: Any => Int = ???
    def foo2: Seq[Any] => Seq[Int] = _.map(foo1)
}
class B {
    private def foo1: Any => Int = ???
    private def foo2: Seq[Any] => Seq[Int] = _.map(foo1)
}

然后我终于找到方法及其相对名称(也取决于scala版本):

scala 2.12.4:
A => (...)
     public scala.Function1 com.toto.A.foo1() -> getName: foo1
     public scala.Function1 com.toto.A.foo2() -> getName: foo2
B => (...)
     public scala.Function1 com.toto.B.foo1() -> getName: foo1
     public scala.Function1 com.toto.B.foo2() -> getName: foo2

scala 2.11.8:
A => (...)
     public scala.Function1 com.toto.A.foo1() -> getName: foo1
     public scala.Function1 com.toto.A.foo2() -> getName: foo2
B => (...)
     public scala.Function1 com.toto.B.com$toto$B$$foo1() -> getName: com$toto$B$$foo1
     public scala.Function1 com.toto.B.foo2() -> getName: foo2

我在scala文档中搜索了可能导致B中编译的类scala-2.11.8的这个特定名称的内容,但找不到任何相关内容。

有人可以向我解释或重定向到有用的资源吗?提前谢谢!

1 个答案:

答案 0 :(得分:3)

简短回答

foo1必须在字节码中是公共的,因为它是从另一个表示lambda的类引用的,因此必须重命名,以便原始名称可以在子类中使用。

更长的回答

正如您已经发现的那样,代码的细节在这里很重要。特别重要的是,从lambda的身体引用foo1非常重要。

在Scala 2.12中,大多数lambdas可以在编译时不用创建单独的类来表示,但在Scala 2.11中,我们没有使用JDK 8的lambda支持,因此scalac必须发出一个额外的,单独的类来表示lambda。

因此,源代码中foo1的{​​{1}}必须在字节码中声明为private,否则无法从代表该字节码的类中访问它拉姆达。

因为public现在是字节码中的foo1,所以必须给它一个不同于public的名称,因为它必须允许foo1的子类拥有自己的名为B的成员,与超类中私有定义的成员无关。

换句话说,我需要仍然可以在以后写一些其他编译单元:

foo1

class C extends B { def foo1 = ... } 中的公开foo1C中的私人foo1无关,因此,重命名B.&#39} B是强制性的,因为我们无法为这两种方法赋予相同的名称,而且我们更倾向于破坏私人名称而不是公开名称。

元回答

关于你可以从中学到什么的元问题,我不知道。

如果http://docs.scala-lang.org/overviews/index.html处有一个文件记录了scalac生成的字节码中可能出现的特殊或意外情况,那将是很可爱的。 (关心的原因:反思,Java互操作,Java序列化,性能......?)

但是,没有这样的文件存在。所以为了自己想出这个,你可能需要一些组合:

  • 实验以缩小重命名的条件
  • 从第一原则推断为什么有必要重命名
  • 挖掘编译器的源代码