課程內(nèi)容涵蓋了Java互操作性。
javap的是JDK附帶的一個工具。不是JRE,這里是有區(qū)別的。 javap反編譯類定義,給你展示里面有什么。用法很簡單
[local ~/projects/interop/target/scala_2.8.1/classes/com/twitter/interop]$ javap MyTrait
Compiled from "Scalaisms.scala"
public interface com.twitter.interop.MyTrait extends scala.ScalaObject{
public abstract java.lang.String traitName();
public abstract java.lang.String upperTraitName();
}
如果你是底層控可以看看字節(jié)碼
[local ~/projects/interop/target/scala_2.8.1/classes/com/twitter/interop]$ javap -c MyTrait\$class
Compiled from "Scalaisms.scala"
public abstract class com.twitter.interop.MyTrait$class extends java.lang.Object{
public static java.lang.String upperTraitName(com.twitter.interop.MyTrait);
Code:
0: aload_0
1: invokeinterface #12, 1; //InterfaceMethod com/twitter/interop/MyTrait.traitName:()Ljava/lang/String;
6: invokevirtual #17; //Method java/lang/String.toUpperCase:()Ljava/lang/String;
9: areturn
public static void $init$(com.twitter.interop.MyTrait);
Code:
0: return
}
如果你搞不清為什么程序在Java上不起作用,就用javap看看吧!
在Java中使用Scala?類?時要考慮的四個要點
我們將構(gòu)建一個簡單的Scala類來展示這一系列實體
package com.twitter.interop
import java.io.IOException
import scala.throws
import scala.reflect.{BeanProperty, BooleanBeanProperty}
class SimpleClass(name: String, val acc: String, @BeanProperty var mutable: String) {
val foo = "foo"
var bar = "bar"
@BeanProperty
val fooBean = "foobean"
@BeanProperty
var barBean = "barbean"
@BooleanBeanProperty
var awesome = true
def dangerFoo() = {
throw new IOException("SURPRISE!")
}
@throws(classOf[IOException])
def dangerBar() = {
throw new IOException("NO SURPRISE!")
}
}
class SimpleClass(acc_: String) {
val acc = acc_
}
這使得它在Java代碼中就像其他常量一樣可以被訪問
foo$_eq("newfoo");
你可以通過@BeanProperty注解val和var定義。這會按照POJO定義生成getter/setter方法。如果你想生成isFoo方法,使用BooleanBeanProperty注解。丑陋的foo$_eq將變?yōu)?/p>
setFoo("newfoo");
getFoo();
Scala沒有像java那樣有受檢異常(checked exception)。需不需要受檢異常是一個我們不會進入的哲學(xué)辯論,不過當(dāng)你需要在Java中捕獲它時就?很重要?了。dangerFoo和dangerBar將演示這一點。在Java中不能這樣做
// exception erasure!
try {
s.dangerFoo();
} catch (IOException e) {
// UGLY
}
Java會抱怨說 s.dangerFoo從未拋出過 IOException異常。我們可以通過捕獲Throwable來跳過,但是這樣不好。
相反,作為一個良好的Scala公民,可以很體面地像在dangerBar中那樣使用throws注解。這使我們能夠繼續(xù)在Java中使用受檢異常。
支持Java互操作的Scala注解的完整列表在這里?http://www.scala-lang.org/node/106。
你如何獲得一個接口+實現(xiàn)?讓我們看一個簡單的特質(zhì)定義
trait MyTrait {
def traitName:String
def upperTraitName = traitName.toUpperCase
}
這個特質(zhì)有一個抽象方法(traitName)和一個實現(xiàn)的方法(upperTraitName)。Scala為我們生成了什么呢?一個名為MyTrait的的接口,和一個名為MyTrait$class的實現(xiàn)類。
MyTrait和你期望的一樣
[local ~/projects/interop/target/scala_2.8.1/classes/com/twitter/interop]$ javap MyTrait
Compiled from "Scalaisms.scala"
public interface com.twitter.interop.MyTrait extends scala.ScalaObject{
public abstract java.lang.String traitName();
public abstract java.lang.String upperTraitName();
}
MyTrait$class更有趣
[local ~/projects/interop/target/scala_2.8.1/classes/com/twitter/interop]$ javap MyTrait\$class
Compiled from "Scalaisms.scala"
public abstract class com.twitter.interop.MyTrait$class extends java.lang.Object{
public static java.lang.String upperTraitName(com.twitter.interop.MyTrait);
public static void $init$(com.twitter.interop.MyTrait);
}
MyTrait$class只有以MyTrait實例為參數(shù)的靜態(tài)方法。這給了我們一個如何在Java中來擴展一個特質(zhì)的提示。
首先嘗試下面的操作
package com.twitter.interop;
public class JTraitImpl implements MyTrait {
private String name = null;
public JTraitImpl(String name) {
this.name = name;
}
public String traitName() {
return name;
}
}
我們會得到以下錯誤
[info] Compiling main sources...
[error] /Users/mmcbride/projects/interop/src/main/java/com/twitter/interop/JTraitImpl.java:3: com.twitter.interop.JTraitImpl is not abstract and does not override abstract method upperTraitName() in com.twitter.interop.MyTrait
[error] public class JTraitImpl implements MyTrait {
[error] ^
我們?可以?自己實現(xiàn)。但有一個鬼鬼祟祟的方式。
package com.twitter.interop;
public String upperTraitName() {
return MyTrait$class.upperTraitName(this);
}
我們只要把調(diào)用代理到生成的Scala實現(xiàn)上就可以了。如果愿意我們也可以覆蓋它。
單例對象是Scala實現(xiàn)靜態(tài)方法/單例模式的方式。在Java中使用它會有點奇怪。沒有一個使用它們的完美風(fēng)格,但在Scala2.8中用起來并不很糟糕
一個Scala單例對象會被編譯成由“$”結(jié)尾的類。讓我們創(chuàng)建一個類和一個伴生對象
class TraitImpl(name: String) extends MyTrait {
def traitName = name
}
object TraitImpl {
def apply = new TraitImpl("foo")
def apply(name: String) = new TraitImpl(name)
}
我們可以像這樣天真地在Java中訪問
MyTrait foo = TraitImpl$.MODULE$.apply("foo");
現(xiàn)在你可能會問自己,這是神馬玩意?這是一個正常的反應(yīng)。讓我們來看看TraitImpl$里面實際上是什么
local ~/projects/interop/target/scala_2.8.1/classes/com/twitter/interop]$ javap TraitImpl\$
Compiled from "Scalaisms.scala"
public final class com.twitter.interop.TraitImpl$ extends java.lang.Object implements scala.ScalaObject{
public static final com.twitter.interop.TraitImpl$ MODULE$;
public static {};
public com.twitter.interop.TraitImpl apply();
public com.twitter.interop.TraitImpl apply(java.lang.String);
}
其實它里面沒有任何靜態(tài)方法。取而代之的是一個名為MODULE$的靜態(tài)成員。方法實現(xiàn)被委托給該成員。這使得訪問代碼很難看,但卻是可行的。
在Scala2.8中處理單例對象變得相對容易一點。如果你有一個類與一個伴生對象,2.8編譯器會生成轉(zhuǎn)發(fā)方法在伴生類中。所以,如果你用2.8,你可以像這樣調(diào)用TraitImpl單例對象的方法
MyTrait foo = TraitImpl.apply("foo");
Scala的最重要的特點之一,就是把函數(shù)作為頭等公民。讓我們來定義一個類,它定義了一些以函數(shù)作為參數(shù)的方法。
class ClosureClass {
def printResult[T](f: => T) = {
println(f)
}
def printResult[T](f: String => T) = {
println(f("HI THERE"))
}
}
在Scala中可以像這樣調(diào)用
val cc = new ClosureClass
cc.printResult { "HI MOM" }
在Java中卻不那么容易,不過也并不可怕。讓我們來看看ClosureClass實際上被編譯成什么:
[local ~/projects/interop/target/scala_2.8.1/classes/com/twitter/interop]$ javap ClosureClass
Compiled from "Scalaisms.scala"
public class com.twitter.interop.ClosureClass extends java.lang.Object implements scala.ScalaObject{
public void printResult(scala.Function0);
public void printResult(scala.Function1);
public com.twitter.interop.ClosureClass();
}
這也不是那么恐怖。 “f: => T”被轉(zhuǎn)義成“Function0”,“f: String => T”被轉(zhuǎn)義成“Function1”。Scala實際上從Function0定義到Function22,最多支持22個參數(shù)。這真的應(yīng)該足夠了。
現(xiàn)在我們只需要弄清楚如何在Java中使用這些東東。我們可以傳入Scala提供的AbstractFunction0和AbstractFunction1,像這樣
@Test public void closureTest() {
ClosureClass c = new ClosureClass();
c.printResult(new AbstractFunction0() {
public String apply() {
return "foo";
}
});
c.printResult(new AbstractFunction1<String, String>() {
public String apply(String arg) {
return arg + "foo";
}
});
}
注意我們可以使用泛型參數(shù)。
Built at?@twitter?by?@stevej,?@marius, and?@lahosken?with much help from?@evanm,?@sprsquish,?@kevino,?@zuercher,?@timtrueman,?@wickman, and@mccv; Russian translation by?appigram; Chinese simple translation by?jasonqu; Korean translation by?enshahar;
Licensed under the?Apache License v2.0.
更多建議: