Chapter 31《Combining Scala and Java》

从Java角度看Scala

  • scala一般要和Java在大型程序中使用,使用Java中的框架。Scala的实现方式是将代码翻译成为标准的Java字节码,Scala的特性尽可能地直接映射为Java中的特性。可以使用javap来查看.classScala进行的翻译。

1. 值类型

对于值类型,在能够确定的情况下,优先翻译成为Java中的值类型用以获得更好的性能,如果不能确定,则翻译为包装类型。

2. 单例对象

  • 采用静态和实例方法相结合的方式来翻译单例对象,如果是单个的对象,则生成类Test$Test
public final class Test$ {
  public static final Test$ MODULE$;
  public static {};
  public void main(java.lang.String[]);
  public java.lang.String select(java.lang.String[]);
}

其中,编译器插入了代码,保证在运行的时候私有化了Test$的构造方法,并对MODULE$进行了赋值。在Test类中

public final class Test {
  public static java.lang.String select(java.lang.String[]);
  public static void main(java.lang.String[]);
}

Test$中的所有方法在Test中都存在有static的转发版本。在书中说如果object有了对应的class,则在javaclass中不会添加额外的转发方法,但是在实际过程中发现即使有了对应的classjava中的class也添加了额外的转发方法。

3.接口

  • 特质被翻译成为接口。如果在特质中有实现的部分,使用abstract class进行实现。

注解

  • 主要讨论的是Java中的注解。如果是@deprecated,则在Java代码上也加入该注解,这样,当Java代码试图访问Scala中带有@deprecated的代码时,会抛出警告。@volatile也是同样的操作。对与序列化的注解,Scala中的@serializableJava中会翻译为实现了Serializable接口,@SerialVersionUID(1234L)被翻译成为
// Java serial version marker
private final static long SerialVersionUID = 1234L

@transient也是在Java代码上加上同样的注解。

1.异常抛出

  • Scala并不检查是由否有异常抛出,所以转换得到的所有Java代码都是不带throw的。因为在Java中,大程序可能就是吞下并隐藏了许多异常,所以在Scala中并不采用这种throwcatch的方法进行处理。如果生成的Java代码中必须要有throws异常的语法,则在scala中使用@throws的注解,
class Reader(fname: String) {
    private val in =
        new BufferedReader(new FileReader(fname))
    @throws(classOf[IOException])
    def read() = in.read()
}

2.Java注解

现在的Java框架注解都可以直接应用在Scala代码中。

3.定制注解

如果需要注解在Java反射的时候可以看到,则必须使用Java中的注解,并使用javac来编译它。因为Scala不能完全的支持Java中的注解,使用的反射机制也是Java的。可以这么使用:

import java.lang.annotation.*; // This is Java
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Ignore { }

object Tests {
  @Ignore
  def testData = List(0, 1, -1, 5, -5)
  def test1 = {
      assert(testData == (testData.head :: testData.tail))
  }
  def test2 = {
      assert(testData.contains(testData.head))
  }
}
for {
  method <- Tests.getClass.getMethods
  if method.getName.startsWith("test")
  if method.getAnnotation(classOf[Ignore]) == null // 使用的是Java中的反射API,所以比较使用的是null
} {
  println("found a test method: " + method)
}

使用的顺序如下所示:

$ javac Ignore.java
$ scalac Tests.scala
$ scalac FindTests.scala
$ scala FindTests
found a test method: public void Tests$.test2()
found a test method: public void Tests$.test1()

注意到这里是Tests$而不是Tests,因为使用的是Java中的反射API,所以在Tests.getClass.getMethods的时候类就是Tests$,而不是Test,同时注意在Java中的注解参数只能是常量,不能是x*2x是常量这种形式。

通配符类型

  • JavaScala是相通的,一般的转换都是非常简单的,Java中的Iterator 就是Scala中的Iterator[Component],但是如果在Java中存在有通配符的泛型,比如Iterator或者Iterator这样的类型,在Scala中有同样的成为通配符的进行匹配转换。使用的是占位符的思想,使用下划线作为通配符进行转换,Iterator就是Iterator[_],表示一个Iterator,但是其中的类型是不明的。同样地,可以插入上限符号和下限符号来限制范围。Iterator[_ <: Father],表示必须是Father的子类,Iterator[_ >: Child],表示必须是Child的父类。

同时编译Scala代码和Java代码

  • 通常如果Scala代码依赖Java代码的话,首先编译Java代码生成对应的class文件,再编译Scala文件,将Java class文件放入到classpath中,如果在Java中同样引用了Scala代码,那么这个方法就失效了,Scala提供了一种解决这个问题的方法。

在Scala2.12中集成Java8

  • Java8中,任意一个需要一个类或者接口对象的地方都可以使用lamda表达式替代,这个类或者接口只能含有一个抽象的方法,叫Single Abstract Method对象,在Scala2.12中对SAM进行子类实现的时候,可以直接使用一个函数代替匿名类的实例。

在Scala2.12中使用Java8中的流

  • Java中的Stream是一个函数数据结构,提供了一个map方法。

你可能感兴趣的:(Chapter 31《Combining Scala and Java》)