與Java互操作

來源:互聯網
上載者:User

標籤:

1. 在Scala裡使用Scala類

如果在單獨的檔案裡建立Scala類,就可以輕鬆地使用它們,就像(無需顯式編譯)在Scala指令碼裡使用一樣。不過,如果想在編譯過的Scala或Java代碼裡使用Scala類,那就必須編譯了。

舉例如下:

package com.cn.pengclass Person(val firstName:String, val lastName:String) {    override def toString():String = firstName + " " + lastName}
package com.cn.pengclass Dog(name:String) {    override def toString():String = name}

下面是使用上面兩個類的指令碼:

package com.cn.pengobject use {  def main(args:Array[String]){    val george = new Person("George", "Washington")        val geogesDogs = List(new Dog("Captain"), new Dog("Clode"),        new Dog("Forester"), new Dog("Searcher"))            printf("%s had several dogs %s...", george,geogesDogs mkString ", ")  }    }

指令碼會產生如下輸出:

George Washington had several dogs Captain, Clode, Forester, Searcher...
 2. 在Scala裡使用Java類

在Scala裡可以直接使用Java類。如果要用的Java類是標準JDK的一部分,直接用就是了。如果它不在java.lang裡,就要匯入類的包。下面用到了java.util和java.lang.reflect包:

package com.cn.pengimport java.util.Dateimport java.lang.reflect._object UseJDKClasses extends App {    println("Today is " + new Date())        val methods = getClass.getMethods()    methods.foreach{method:Method => println(method.getName())}}

程式運行結果如下:

Today is Sat Apr 04 16:53:55 CST 2015maindelayedEndpoint$com$cn$peng$UseJDKClasses$1argsdelayedInitscala$App$_setter_$executionStart_$eqexecutionStartscala$App$$_argsscala$App$$initCodemethodsscala$App$$_args_$eqscala$App$_setter_$scala$App$$initCode_$eqwaitwaitwaitequalstoStringhashCodegetClassnotifynotifyAll

如果想用的Java類是你自己建立的,或是來自第三方,請確保scalac的classpath指向位元組碼的位置。假定我們有如下的Java檔案:

package investments;public enum InvestmentType {    SHORT_TERM,    BOND,    STOCK,    REAL_ESTATE,    COMMODITIES,    COLLECTIBLES,    MUTUAL_FUNDS}
package investments;public class Investment {    private String investmentName;    private InvestmentType investmentType;        public Investment(String name, InvestmentType type){        investmentName = name;        investmentType = type;    }        public int yield() { return 0; }}

在Scala代碼裡使用這些類,同使用Scala類一樣。下面是一個在Scala裡建立Investment執行個體的例子:

package com.cn.pengimport investments._object UseInvestment {    def main(args: Array[String]){      val investment = new Investment("XYZ Corporation", InvestmentType.STOCK)      println(investment.getClass())    }}

運行結果如下:

class investments.Investment

Investment類的yield()方法需要小心使用。如果Java代碼有方法或欄位的名字(比如trait或yield等)於Scala的關鍵字衝突,調用它們會導致Scala編譯器死掉。比如,下面的代碼是不行的:

      val theYield1 = investment.yield   //ERROR      val theYield2 = investment.yield() //ERROR

幸運的是,Scala提供了一個解決方案。把衝突的變數/方法放到反引號裡,就可以繞開這個問題。改一下代碼就可以讓上面的兩個調用工作了:

      val theYield1 = investment.`yield`         val theYield2 = investment.`yield`()
3. 在Java裡使用Scala類

Scala提供了與Java之間完整的雙向互通性。因為Scala能編譯成位元組碼,所以在Java裡使用Scala類相當容易。預設情況下,Scala並不遵循JavaBean的約定,要用@scala.reflect.BeanProperty這個註解產生符合JavaBean預定的getter和setter。還可以從Scala類繼承Java類,不過,要運行使用了Scala類的Java代碼,classpath裡需要有scala-library.jar。在本節裡,我們會看到Scala的構造在Java端會表現出怎樣的不同。

3.1 有普通函數和高階函數的Scala類

遵循標準Java構造的Scala類相當直白,在Java端使用它們很容易。我們寫一個Scala類:

package automobilesclass Car(val year:Int) {    private[this] var miles : Int = 0        def drive(distance:Int) {miles += distance}        override def toString():String = "year: " + year + " miles: " + miles}

下面是個使用這個Scala類的Java類:

 

package com.cn.peng;import automobiles.Car;public class UseCar {    public static void main(String[] args){        Car car = new Car(2014);        System.out.println(car);        car.drive(10);        System.out.println(car);    }}

程式運行結果如下:

year: 2014 miles: 0year: 2014 miles: 10

在Java裡使用Scala類相當簡單。不過,不是所有的Scala類都那麼友善。比如,如果Scala類有方法接收閉包,這些方法在Java裡就不可用,因為Java目前尚不支援閉包。下面Equipment類的simulate()方法對Java就是停用;不過,我們可以用run()方法:

package com.cn.pengclass Equipment {    //Not usable from Java  def simulate(input:Int)(calculator:Int => Int):Int = {    //...    calculator(input)  }    def run(duration:Int){    println("running")    //...  }}

因此,設計API的時候,如果類主要是給Java用,請在提供高階函數的同時也提供普通函數,讓這個類對Java完全可用。

3.2 同trait一起工作

沒有方法實現的trait在位元組碼層面上就是簡單的介面。Scala不支援interface關鍵字。因此,如果想在Scala裡建立介面,就建立一個沒有實現的trait。下面是個Scala trait的例子,他也是個介面:

package com.cn.pengtrait Writable {    def wirte(message: String):Unit}

上面的trait裡面有個抽象的方法,混入這個trait的類都應該實現這個方法。在Java端,Writable可以看做與其它介面一樣;它對Scala根本沒有依賴。所以,可以這樣實現(implement)它:

package com.cn.peng;public class AWritableJavaClass implements Writable{    @Override    public void wirte(String message) {        // TODO Auto-generated method stub            }}

不過,如果trait有方法實現,那麼Java類就不能實現這個trait/interface,雖然它們可以使用它。因此,在Java裡不能實現下面的Printable,但可以持有一個Printable的引用:

package com.cn.pengtrait Printable {    def print(){} //default print nothing}

如果想讓Java類實現trait,就讓它純粹些;換句話說,不要有實現。在這種情況下,任何公用的實現都應該放到抽象基類裡,而不是trait裡。不過,如果只是想讓Java類使用trait,就沒有任何限制。

3.3 單例對象和伴生對象

Scala將對象(單例對象或伴生對象)編譯成一個“單例類”——這個類的名字末尾有一個特殊$符。這樣,下面所示的Object Single,會產生一個類名Single$。不過,Scala處理單例對象和伴生對象有些不同,稍後可以看到。

Scala把單例對象編譯到一個單例類(它用的是Java的靜態方法)中,此外,還會建立一個普通的類,它把調用傳遞給單例類。所以,下面這段代碼建立了一個單例對象Single,而Scala則建立了兩個類:Single$和用來傳遞調用的類Single。

package com.cn.pengobject Single {    def greet(){println("Hello from Single")}}

在Java裡使用上面的單例對象,就像使用由static方法的Java類一樣,如下所示:

package com.cn.peng;public class SingleUser {    public static void main(String[] args){        Single.greet();    }}

上面代碼的輸出如下:

Hello from Single

如果對象是同名類的伴生對象,Scala會建立兩個類,一個類表示Scala類(下面例子裡的Buddy),另一個類表示伴生對象(下面例子裡的Buddy$):

package com.cn.pengclass Buddy {    def greet(){println("Hello from Buddy class")}}object Buddy {    def greet(){println("Hello from Buddy object")}}

訪問伴生類可以直接使用類的名字。訪問伴生對象需要使用特殊的符號MODULE$,如下例所示:

package com.cn.peng;public class BuddyUser {    public static void main(String[] args){        new Buddy().greet();        Buddy$.MODULE$.greet();    }}

輸出如下:

Hello from Buddy classHello from Buddy object
4. 繼承類

Scala類可以繼承Java類,反之亦然。大多數情況下,這應該夠用了。之前也討論過,如果方法接收閉包作為參數,重寫起來就有些麻煩。異常也是這個問題。

Scala沒有throws字句。在Scala裡,任意方法都可以拋出異常,無需顯式聲明成方法簽名的一部分。不過,如果在Java裡重寫這樣的方法,試圖拋出異常,就會陷入麻煩。看個例子,假設Scala定義了Bird:

package com.cn.pengabstract class Bird {    def fly();    //...}

還有另一個類Ostrich:

package com.cn.pengclass Ostrich extends Bird {    def fly(){      throw new NoFlyException    }    //...}

其中NoFlyException定義如下:

package com.cn.pengclass NoFlyException extends Exception{}

在上面的代碼裡,Ostrich的fly()拋出異常沒有任何問題。不過,如果要在Java裡實現一個不能飛的鳥,就會有麻煩,如下所示:

package com.cn.peng;public class Penguin extends Bird{    public void fly() throws NoFlyException {        throw new NoFlyException();    }    //...}

首先,如果只是拋出異常,Java會報錯“unreported exception NoFlyException;must be caught or declared to be thrown." 一旦加上了throws子句,java又會報錯”Exception NoFlyException is not compatible with throws clause in Bird.fly()“。

即便Scala很靈活,並不強求一定要指定拋出哪些異常,但是要想在Java裡繼承這些方法,就要告訴Scala編譯器,把這些細節記錄在方法簽名裡。Scala為此提供了一個後門:定義@throws註解。

雖然Scala支援註解,但它卻不提供註解的文法。如果想建立自己的註解,就不得不用Java來做。@throws是已經提供好的註解,用以表示方法拋出的受控異常。這樣,對我們來說,要在Java裡實現Penguin,必須在把Bird改成這樣:

package com.cn.pengabstract class Bird {  @throws(classOf[NoFlyException]) def fly();    //...}

現在,編製上面的代碼,Scala編譯器會在位元組碼裡為fly()方法放上必要的簽名。經過了這個修改,Java類Penguin就可以正常編譯了。

與Java互操作

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.