2016-11-17 74 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需要序列化它們。