为什么 scala.beans.beanproperty 在 spark 中的工作方式不同
Why does scala.beans.beanproperty work differently in spark
在 Scala REPL 中使用以下代码
import scala.beans.BeanProperty
class EmailAccount {
@scala.beans.BeanProperty var accountName: String = null
override def toString: String = {
return s"acct ($accountName)"
}
}
classOf[EmailAccount].getDeclaredConstructor()
结果
res0: java.lang.reflect.Constructor[EmailAccount] = public EmailAccount()
但是在 spark 的 REPL 中我得到
java.lang.NoSuchMethodException: EmailAccount.<init>()
at java.lang.Class.getConstructor0(Class.java:2810)
at java.lang.Class.getDeclaredConstructor(Class.java:2053)
... 48 elided
造成这种差异的原因是什么?我怎样才能得到火花来匹配火花的行为 shell.
我是这样启动 REPL 的:
/home/placey/Downloads/spark-2.0.0-bin-hadoop2.7/bin/spark-shell --master local --jars /home/placey/snakeyaml-1.17.jar
和
scala -classpath "/home/placey/snakeyaml-1.17.jar
Scala 版本是
火花:
Using Scala version 2.11.8 (Java HotSpot(TM) 64-Bit Server VM, Java 1.7.0_55)
scala:
Welcome to Scala version 2.11.6 (Java HotSpot(TM) 64-Bit Server VM, Java 1.7.0_55).
实际上,这并非特定于 scala.beans.BeanProperty
甚至 Spark。您可以通过 运行 使用 -Yrepl-class-based
参数在标准 Scala REPL 中获得相同的行为:
scala -Yrepl-class-based
现在,让我们尝试定义一个简单的空 class:
scala> class Foo()
defined class Foo
scala> classOf[Foo].getConstructors
res0: Array[java.lang.reflect.Constructor[_]] = Array(public Foo($iw))
scala> classOf[Foo].getFields
res1: Array[java.lang.reflect.Field] = Array(public final $iw Foo.$outer)
如您所见,REPL 通过向构造函数添加额外的字段和参数来即时修改您的 class。为什么?
每当你在 Scala REPL 中创建一个 val
或 var
时,它都会被包裹在一个特殊的对象中,因为在 Scala 中没有 "global variables" 这样的东西。参见 this answer。
通常情况下,这是一个对象,所以它是全局可用的。但是,对于 -Yrepl-class-based
,REPL 使用 class 个实例而不是单个全局对象。此功能由 Spark 开发人员引入,因为 Spark 需要 classes 可序列化,以便将它们发送给远程工作者(请参阅 this pull request)。
因此,您在 REPL 中定义的任何 class 都需要获取 $iw
实例。否则,您将无法访问您在 REPL 中定义的全局 val
s 和 var
s。此外,生成的 class 会自动扩展 Serializable
.
恐怕你无法阻止这种情况。 spark-shell
默认启用 -Yrepl-class-based
。即使有禁用此行为的选项,您也会 运行 遇到许多其他问题,因为您的 classes 将不再可序列化,但 Spark 需要序列化它们。
在 Scala REPL 中使用以下代码
import scala.beans.BeanProperty
class EmailAccount {
@scala.beans.BeanProperty var accountName: String = null
override def toString: String = {
return s"acct ($accountName)"
}
}
classOf[EmailAccount].getDeclaredConstructor()
结果
res0: java.lang.reflect.Constructor[EmailAccount] = public EmailAccount()
但是在 spark 的 REPL 中我得到
java.lang.NoSuchMethodException: EmailAccount.<init>()
at java.lang.Class.getConstructor0(Class.java:2810)
at java.lang.Class.getDeclaredConstructor(Class.java:2053)
... 48 elided
造成这种差异的原因是什么?我怎样才能得到火花来匹配火花的行为 shell.
我是这样启动 REPL 的:
/home/placey/Downloads/spark-2.0.0-bin-hadoop2.7/bin/spark-shell --master local --jars /home/placey/snakeyaml-1.17.jar
和
scala -classpath "/home/placey/snakeyaml-1.17.jar
Scala 版本是 火花:
Using Scala version 2.11.8 (Java HotSpot(TM) 64-Bit Server VM, Java 1.7.0_55)
scala:
Welcome to Scala version 2.11.6 (Java HotSpot(TM) 64-Bit Server VM, Java 1.7.0_55).
实际上,这并非特定于 scala.beans.BeanProperty
甚至 Spark。您可以通过 运行 使用 -Yrepl-class-based
参数在标准 Scala REPL 中获得相同的行为:
scala -Yrepl-class-based
现在,让我们尝试定义一个简单的空 class:
scala> class Foo()
defined class Foo
scala> classOf[Foo].getConstructors
res0: Array[java.lang.reflect.Constructor[_]] = Array(public Foo($iw))
scala> classOf[Foo].getFields
res1: Array[java.lang.reflect.Field] = Array(public final $iw Foo.$outer)
如您所见,REPL 通过向构造函数添加额外的字段和参数来即时修改您的 class。为什么?
每当你在 Scala REPL 中创建一个 val
或 var
时,它都会被包裹在一个特殊的对象中,因为在 Scala 中没有 "global variables" 这样的东西。参见 this answer。
通常情况下,这是一个对象,所以它是全局可用的。但是,对于 -Yrepl-class-based
,REPL 使用 class 个实例而不是单个全局对象。此功能由 Spark 开发人员引入,因为 Spark 需要 classes 可序列化,以便将它们发送给远程工作者(请参阅 this pull request)。
因此,您在 REPL 中定义的任何 class 都需要获取 $iw
实例。否则,您将无法访问您在 REPL 中定义的全局 val
s 和 var
s。此外,生成的 class 会自动扩展 Serializable
.
恐怕你无法阻止这种情况。 spark-shell
默认启用 -Yrepl-class-based
。即使有禁用此行为的选项,您也会 运行 遇到许多其他问题,因为您的 classes 将不再可序列化,但 Spark 需要序列化它们。