当我编译以下代码以观察编译的方法名称不同时,我感到非常惊讶:
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
的这个特定名称的内容,但找不到任何相关内容。
有人可以向我解释或重定向到有用的资源吗?提前谢谢!
答案 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 = ... }
中的公开foo1
与C
中的私人foo1
无关,因此,重命名B.
&#39} B
是强制性的,因为我们无法为这两种方法赋予相同的名称,而且我们更倾向于破坏私人名称而不是公开名称。
元回答
关于你可以从中学到什么的元问题,我不知道。
如果http://docs.scala-lang.org/overviews/index.html处有一个文件记录了scalac生成的字节码中可能出现的特殊或意外情况,那将是很可爱的。 (关心的原因:反思,Java互操作,Java序列化,性能......?)
但是,没有这样的文件存在。所以为了自己想出这个,你可能需要一些组合: