2016-11-17 77 views
1

确实scala.beans.beanproperty工作方式不同。在斯卡拉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 

造成这种差异的原因是什么?我如何获得火花来匹配火花外壳的行为。

我发动像这样的REPLs:

/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 

斯卡拉版本 火花:

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). 

回答

3

实际上,这不是特定于scala.beans.BeanProperty甚至Spark。您可以通过运行它与-Yrepl-class-based参数获得标准斯卡拉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,它都被包装在一个特殊对象中,因为在Scala中没有“全局变量”这样的事情。见this answer

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

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

恐怕你不能做任何事情来阻止这个spark-shell默认启用-Yrepl-class-based。即使有禁用此行为的选项,您也会遇到许多其他问题,因为您的类不再可序列化,但Spark需要序列化它们。