与Java互操作

1. 在Scala里使用Scala类

如果在单独的文件里创建Scala类,就可以轻松地使用它们,就像(无需显式编译)在Scala脚本里使用一样。不过,如果想在编译过的Scala或Java代码里使用Scala类,那就必须编译了。

举例如下:

package com.cn.peng

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

class Dog(name:String) {
    override def toString():String = name
}

下面是使用上面两个类的脚本:

package com.cn.peng

object 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.peng
import java.util.Date
import 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 2015
main
delayedEndpoint$com$cn$peng$UseJDKClasses$1
args
delayedInit
scala$App$_setter_$executionStart_$eq
executionStart
scala$App$$_args
scala$App$$initCode
methods
scala$App$$_args_$eq
scala$App$_setter_$scala$App$$initCode_$eq
wait
wait
wait
equals
toString
hashCode
getClass
notify
notifyAll

如果想用的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.peng
import 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 automobiles

class 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: 0
year: 2014 miles: 10

在Java里使用Scala类相当简单。不过,不是所有的Scala类都那么友善。比如,如果Scala类有方法接收闭包,这些方法在Java里就不可用,因为Java目前尚不支持闭包。下面Equipment类的simulate()方法对Java就是不可用的;不过,我们可以用run()方法:

package com.cn.peng

class 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.peng

trait 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.peng

trait 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.peng

object 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.peng

class 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 class
Hello from Buddy object

4. 继承类

Scala类可以继承Java类,反之亦然。大多数情况下,这应该够用了。之前也讨论过,如果方法接收闭包作为参数,重写起来就有些麻烦。异常也是这个问题。

Scala没有throws字句。在Scala里,任意方法都可以抛出异常,无需显式声明成方法签名的一部分。不过,如果在Java里重写这样的方法,试图抛出异常,就会陷入麻烦。看个例子,假设Scala定义了Bird:

package com.cn.peng

abstract class Bird {
    def fly();
    //...
}

还有另一个类Ostrich:

package com.cn.peng

class Ostrich extends Bird {
    def fly(){
      throw new NoFlyException
    }
    //...
}

其中NoFlyException定义如下:

package com.cn.peng

class 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.peng

abstract class Bird {
  @throws(classOf[NoFlyException]) def fly();
    //...
}

现在,编制上面的代码,Scala编译器会在字节码里为fly()方法放上必要的签名。经过了这个修改,Java类Penguin就可以正常编译了。

时间: 2024-10-10 23:22:57

与Java互操作的相关文章

快学Scala 第五课 (构造映射,获取映射值,更新映射值,迭代映射,与Java互操作)

构造映射: val score = Map[String, Int]() val score1 = HashMap[String, Int]() val value1 = Map[String, Int]("aa" -> 1, "bb" -> 2) val value2 = Map[String, Int](("aa", 1), ("bb", 2)) 获取映射值: println(if(value2.contain

Scala入门到精通——第二十八节 Scala与JAVA互操作

本节主要内容 JAVA中调用Scala类 Scala中调用JAVA类 Scala类型参数与JAVA泛型互操作 Scala与Java间的异常处理互操作 1. JAVA中调用Scala类 Java可以直接操作纵Scala类,如同Scala直接使用Java中的类一样,例如: //在Person.scala文件中定义Scala语法的Person类 package cn.scala.xtwy.scalaToJava class Person(val name:String,val age:Int) //伴

LuaJavaBridge - lua与java互操作的简单解决方案

引入:Android平台代码和Lua代码的交互均通过C++和Java交互,Lua再和C++交互(lua  <==> C++ <==> java) 我最开始遇见这种lua调用java的做法是先用 C/C++ 借助 JNI 编写调用 Java 的接口函数,然后再将这些函数通过 tolua++ 导出给 Lua 使用.这种做法最大的问题就是太繁琐,而且稍微有一点点修改,就要重新编译,严重降低了开发效率. 后来知道了luaj这个东西,发现之前的做法简直太傻帽了. luaj 主要特征: 1.可

cocos2dx-Lua与Java通讯机制

本文转载于:http://dualface.github.io/blog/2013/01/01/call-java-from-lua/ LuaJavaBridge - Lua 与 Java 互操作的简单解决方案 最近在游戏里要集成中国移动的 SDK,而这些 SDK 都是用 Java 编写的.由于我们整个游戏都是使用 Lua 开发的,所以就面对 Lua 与 Java 互操作的问题. 传统做法是先用 C/C++ 借助 JNI(Java Native Interface)编写调用 Java 的接口函数

给Java开发者的Scala教程

author:Michel Schinz,Philipp Haller 1. 简介 本文将该要的介绍Scala语言和其编译.这里假设读者已经有一定的java开发经验,需要概要的了解他们可以用Scala 做些什么. 2. 第一个例子 我们用全世界最著名的代码来作为开始.虽然没什么用,但是可以很好地直观的了解Scala: object HelloWorld { def main(args: Array[String]): Unit = { println("Hello, world!")

Scala 学习

1,Scala学习 官方网网站: http://www.scala-lang.org/ http://www.scala-lang.org/download/ 可伸缩的语言是一种多范式的编程语言,一种类似java的编程,设计初衷是要集成面向对象编程和函数式编程的各种特性. Scala是在JVM上运行. Scala有几项关键特性表明了它的面向对象的本质.例如,Scala中的每个值都是一个对象,包括基本数据类型(即布尔值.数字等)在内,连函数也是对象.另外,类可以被子类化,而且Scala还提供了基于

Scala--数组相关操作

一.定长数组 Array定长数组,访问数组元素需要通过() val nums = new Array[Int](10) //长度为10的int数组 初始化为0 val strs = new Array[String](10) //长度为10的String数组 初始化为null val s = Array("Hello", "World") //初始化数组长度为2,不需要new s(0) = "GoodBye" //访问数组元素通过() print

Spark小象学院笔记

---小象学院陈超视频教程笔记------陈超讲 第一节 Scala基础与实践 基于JVM的FP+OO 静态类型 和Java互操作 函数式编程和面向对象的结合,纯静态的语言. 解释器(interpreter) 值与变量(val & var) 函数(Function) 1.常量 val 2.变量 var 3.main函数要定义在object里面 实例: object Basic{ def hello(name : String): String ={ "Hello :" + na

Scala学习(三)----数组相关操作

数组相关操作 摘要: 本篇主要学习如何在Scala中操作数组.Java和C++程序员通常会选用数组或近似的结构(比如数组列表或向量)来收集一组元素.在Scala中,我们的选择更多,不过现在我们先假定不关心其他选择,而只是想马上开始用数组.本篇的要点包括: 1. 若长度固定则使用Array,若长度可能有变化则使用ArrayBuffer 2. 提供初始值时不要使用new 3. 用()来访问元素 4. 用for (elem<-arr)来遍历元素 5. 用for (elem<-arr if…)…yie