scala - 为什么 Scala 运行时反射不再适用于 lambda?
问题描述
以下简单代码:
import org.scalatest.FunSpec
class RuntimeMirrorSpike extends FunSpec {
import org.apache.spark.sql.catalyst.ScalaReflection.universe._
it("can reflect lambda") {
val ll = { v: String =>
v.toInt
}
val clazz = ll.getClass
val mirror = runtimeMirror(clazz.getClassLoader)
val sym = mirror.classSymbol(clazz)
print(sym)
}
}
曾经在 Scala 2.11 上完美运行。但现在它在 Scala 2.12 上中断了:
assertion failed: no symbol could be loaded from class <...>.spike.RuntimeMirrorSpike$$Lambda$124/78204644 in package spike with name RuntimeMirrorSpike$$Lambda$124/78204644 and classloader sun.misc.Launcher$AppClassLoader@18b4aac2
java.lang.AssertionError: assertion failed: no symbol could be loaded from class <...>.spike.RuntimeMirrorSpike$$Lambda$124/78204644 in package spike with name RuntimeMirrorSpike$$Lambda$124/78204644 and classloader sun.misc.Launcher$AppClassLoader@18b4aac2
at scala.reflect.internal.SymbolTable.throwAssertionError(SymbolTable.scala:184)
at scala.reflect.runtime.JavaMirrors$JavaMirror.classToScala1(JavaMirrors.scala:1061)
at scala.reflect.runtime.JavaMirrors$JavaMirror.$anonfun$classToScala$1(JavaMirrors.scala:1019)
at scala.reflect.runtime.JavaMirrors$JavaMirror.$anonfun$toScala$1(JavaMirrors.scala:130)
at scala.reflect.runtime.TwoWayCaches$TwoWayCache.$anonfun$toScala$1(TwoWayCaches.scala:50)
at scala.reflect.runtime.TwoWayCaches$TwoWayCache.toScala(TwoWayCaches.scala:46)
at scala.reflect.runtime.JavaMirrors$JavaMirror.toScala(JavaMirrors.scala:128)
at scala.reflect.runtime.JavaMirrors$JavaMirror.classToScala(JavaMirrors.scala:1019)
at scala.reflect.runtime.JavaMirrors$JavaMirror.classSymbol(JavaMirrors.scala:231)
at scala.reflect.runtime.JavaMirrors$JavaMirror.classSymbol(JavaMirrors.scala:68)
这里发生了什么?什么样的对象没有运行时类?
解决方案
相对于 2.11,lambda 的编码在 2.12 中发生了变化。请参阅2.12.0 发行说明。
以前,lambdas 的编码方式基本上等同于 Dmytro Mitin 的答案,即扩展 aFunctionN
并具有apply
方法的对象。
在 Java 8 中,引入了 lambda,但方式与 Scala 略有不同。调用 bootstrap 方法,java.lang.invoke.LambdaMetaFactory.metafactory
它将在运行时创建一个类,其中一个方法调用另一个类中的方法。
在 Scala 2.12 中,Scala lambdas 的编码移动以匹配 Java 实现(可能是因为 JVM(尤其是 HotSpot)识别出结果的LambdaMetaFactory.metafactory
结果是 lambda 并且比其他方式进行了更多的优化)。但是,运行时生成的类似乎无法映射到 Scala 类,导致 Scala 的反射被炸毁。
编辑:在 Dmytro Mitin 的评论之后,对我来说唯一明显的解决方法是将 lambdas 手动编码为 的实例FunctionN
:
def delambdafy[Result](ll: () => Result): Function0[Result] = new Function0[Result] { def apply(): Result = ll() }
def delambdafy[In, Result](ll: In => Result): Function1[In, Result] =
new Function1[In, Result] { def apply(in: In): Result = ll(in) }
等等对于必要的arities...
为 REPL 调整您的代码,这有效:
// Entering paste mode (ctrl-D to finish)
import scala.reflect.runtime.universe._ // Effectively what is being imported with your Spark import
def delambdafy[In, Result](ll: In => Result): Function1[In, Result] = new Function1[In, Result] { def apply(in: In): Result = ll(in) }
val ll: String => Int = { v: String => v.toInt }
val clazz = delambdafy(ll).getClass
val symbol = runtimeMirror(clazz.getClassLoader).classSymbol(clazz)
// Exiting paste mode, now interpreting.
import scala.reflect.runtime.universe._
delambdafy: [In, Result](ll: In => Result)In => Result
ll: String => Int = $$Lambda$5162/1405722527@114a083b
clazz: Class[_ <: String => Int] = class $anon$1
symbol: reflect.runtime.universe.ClassSymbol = $anon
是否$anon
对您有用取决于您实际尝试做的事情,这导致您提出这个问题。
推荐阅读
- javascript - How to increase the microphone sample rate on problematic phones
- django - Database: new table or new field for order attributes
- elixir - Elixir bitstring contains sub-bitstring
- python-3.x - cron 调用时避免在不同位置重复执行相同的 python 脚本
- tensorflow - TensorFlow 对象检测——增加批量大小会导致失败
- docker - Vglrun 在 Docker 容器中不起作用
- c - 在 C 中编写基本的字符串加密程序时遇到问题 - 索引字符数组
- pagespeed - 我可以做些什么来提高主要使用 OpenStreetMap 的网站的速度
- android - Can I disable R8 just for some flavors without using command line?
- ruby-on-rails - How can I run two instances of a rails app from 2 different git branches?