• 与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就可以正常编译了。

  • 相关阅读:
    hdu 4525(数学)
    hdu 4524(模拟)
    hdu 4523(大整数)
    hdu 4517(递推枚举统计)
    hdu 4520
    hdu 4519(数学题)
    hdu 4514(树的直径+并查集)
    hdu 4510(模拟)
    hdu 2089(数位DP)
    hdu 4506(数学,循环节+快速幂)
  • 原文地址:https://www.cnblogs.com/gaopeng527/p/4392244.html
Copyright © 2020-2023  润新知