0
点赞
收藏
分享

微信扫一扫

Java + Scala


Javap

javap是JDK附带的一个工具,而不是JRE。它们之间还是有差别的。Javap反编译class文件,并且向你展示它里面放的是什么。使用起来很简单。


[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();         


         }



如果你想了解底层的话,你可以查看对应的字节码

[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的_class_需要注意的四个要点如下:

  • 类参数
  • 类常量
  • 类变量
  • 异常

我们来创建一个简单的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!"         )         


                   }         


         }



类参数

  • 默认情况下,类参数实际上就是Java里构造函数的参数。这就意味着你不能在这个class之外访问它们。
  • 把类参数定义成一个val/var的方式和下面的代码相同

class          SimpleClass(acc_: String) {         


                   val acc = acc_         


         }



下面就可以通过Java代码来访问它们了。

常量(Val)

  • 常量(val)都会定义有对应的供Java代码访问的方法。你可以通过”foo()”方法来取得常量(val)“foo”的值。

变量(Var)

  • 变量(var)会多定义一个_$eq方法。你可以这样调用来设置变量的值:

foo$_eq(         "newfoo"         );



BeanFactory

你可以通过@BeanProperty注解来标注val和var。这样就会生成类似于POJO的getter/setter方法。假如你想要访问isFoo变量,使用BooleanBeanProperty注解。那么难以理解的foo$eq就可以换成:

setFoo(         "newfoo"         );         


         getFoo();



异常

Scala里没有受检异常(checked exception),但是Java里有。这是一个语言层面上的问题,我们这里不进行讨论,但是在Java里你对异常进行捕获的时候你还是要注意的。dangerFoo和dangerBar的定义里对这进行了示范。在Java里,你不能这样做。

// exception erasure!        


         // 异常擦除!        


          try          {         


                   s.dangerFoo();         


          }          catch          (IOException e) {         


                   // UGLY         


                   // 非常丑陋         


         }



Java编译器会因为s.dangerFoo不会抛出IOException而报错。我们可以通过捕获Throwable来绕过这个错误,但是这样做没多大用处。

不过,作为一个Scala用户,比较正式的方式是使用throws注解,就像我们之前在dangerBar上的一样。这个手段使得我们能够使用Java里的受检异常(checked exception)。

延伸阅读

支持Java互操作的注解的完整列表在http://www.scala-lang.org/node/106。

Trait

我们怎样可以得到一个接口和对应的实现呢?我们简单看看trait的定义


trait MyTrait {        


                   def traitName:String         


                   def upperTraitName = traitName.toUpperCase         


         }



这个trait有一个抽象的方法(traitName)和一个已实现的方法(upperTraitName)。对于这样的trait,Scala会生成什么样的代码呢?它会生成一个MyTrait接口,同时还会生成对应的实现类MyTrait$class。

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实例作为参数的方法。这样给了我们在Java如何实现trait的一条线索。

首先要做的是:

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]        ^



我们_可以_自己来实现他们。不过还有一种更加诡异的方式。

package          com.twitter.interop;         


                  


                   public          String upperTraitName() {         


                   return          MyTrait$         class         .upperTraitName(         this         );         


                   }




我们只需要把相应的方法调用代理到Scala的实现上。并且我们还可以根据实际需要进行重写。

对象

在Scala里,是用对象来实现静态方法和单例模式的。如果在Java里使用它们就会显得比较怪。在语法上没有什么比较优雅的方式来使用它们,但是在Scala 2.8里就没有那么麻烦了。

Scala对象会被编译成一个名称带有“$”后缀的类。我们来创建一个类以及对应的对象(Object)。我们来创建一个类以及对应的伴生对象(companion object)。

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"         );


现在你也许会问自己,这究竟是神马?这是一个很正常的反应。我们现在一起来看看TraintImpl$内部究竟是怎么实现的。

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


         }



其实它里面没有任何静态方法。相反,它还有一个静态成员叫做MODULE$。实际上方法的调用都是代理到这个成员变量上的。这种实现使得访问起来觉得比较恶心,但是如果你知道怎么使用MODULE$的话,其实还是很实用的。

转发方法(Forwarding Method)

在Scala 2.8里,处理Object会比较简单点。如果你有一个类以及对应的伴生对象(companion object),2.8 的编译器会在伴生对象里生成转发方法。如果使用2.8的编译器进行构建,那么你可以通过下面的方法来访问TraitImpl对象:

MyTrait foo = TraitImpl.apply(         "foo"         );




闭包函数

Scala最重要的一个特点就是把函数作为一等公民。我们来定义一个类,它里面包含一些接收函数作为参数的方法。

class          ClosureClass {         


                   def printResult[T](f: => T) = {         


                   println(f)         


                   }         


                  


                   def printResult[T](f: String => T) = {         


                   println(f(         "HI THERE"         ))         


                   }         


         }



在Scala里我可以这样调用:

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” 转换成”Function0″,”f: String => T” 转换成 “Function1″。Scala定义了从Function0到Function22,一直支持到22个参数。这么多确实已经足够了。

现在我们只需要弄清楚,怎么在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() {         


                   public          String apply(String arg) {         


                   return          arg +          "foo"         ;         


                   }         


                   });         


         }



注意我们还可以使用泛型来参数化参数的类型。

译文链接: http://www.importnew.com/6188.html

举报

相关推荐

0 条评论