为什么scala.beans.beanproperty在spark中的工作方式不同

时间:2016-11-17 23:20:00

标签: scala apache-spark javabeans

在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()
然而,在火花的REPL中,我得到了

java.lang.NoSuchMethodException: EmailAccount.<init>()
  at java.lang.Class.getConstructor0(Class.java:2810)
  at java.lang.Class.getDeclaredConstructor(Class.java:2053)
  ... 48 elided

导致这种差异的原因是什么?如何获得火花以匹配火花壳的行为。

我像这样推出了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)

阶:

Welcome to Scala version 2.11.6 (Java HotSpot(TM) 64-Bit Server VM, Java 1.7.0_55).

1 个答案:

答案 0 :(得分:3)

实际上,这并不是scala.beans.BeanProperty甚至Spark所特有的。通过使用-Yrepl-class-based参数运行它,可以在标准Scala REPL中获得相同的行为:

scala -Yrepl-class-based

现在,让我们尝试定义一个简单的空类:

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通过向构造函数添加其他字段和参数来动态修改您的类。为什么呢?

每当你在Scala REPL中创建valvar时,它都会被包装在一个特殊的对象中,因为它没有&#34;全局变量&#34;在斯卡拉。请参阅this answer

通常情况下,这是一个对象,因此它可以在全球范围内使用。但是,使用-Yrepl-class-based,REPL使用类实例而不是单个全局对象。 Spark开发人员介绍了此功能,因为Spark需要类可序列化,因此可以将它们发送给远程工作者(请参阅this pull request)。

因此,您在REPL中定义的任何类都需要获取$iw实例。否则,您将无法访问在REPL中定义的全局valvar。此外,生成的类会自动扩展Serializable

我担心你无法阻止这种情况spark-shell默认启用-Yrepl-class-based。即使有一个禁用此行为的选项,您也会遇到许多其他问题,因为您的类不再可序列化,但Spark需要序列化它们。