第14章——和Java进行互操作

现已存在许多强大的 Scala 库,并与日俱增。开发人员不仅可以在 Scala 中使用这些库, 也可以在 Java 中使用它们。但是要做到这一点,必须要学习一些技巧。 ①

  1. 因为某些库可能没有针对潜在的 Java 用户设计友好的 API,所以可能需要一些潜在适配工作。——译者注

14.1 在 Scala 中使用 Scala 类

Intermixing/Person.scala

class Person(val firstName: String, val lastName: String) {
  override def toString: String = firstName + " " + lastName
}

Intermixing/Dog.scala

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

执行命令

scalac Person.scala Dog.scala
jar cf /tmp/example.jar Person.class Dog.class

Intermixing/UsePerson.scala

val george = new Person("George", "Washington")

val georgesDogs = List(new Dog("Captain"), new Dog("Clode"), new Dog("Forester"), new Dog("Searcher"))

println(s"$george had several dogs ${georgesDogs.mkString(", ")}...")

执行命令

scala -classpath /tmp/example.jar usePerson.scala

运行结果

George Washington had several dogs Captain, Clode, Forester, Searcher...

Intermixing/UsePersonClass.scala

object UsePersonClass extends App {
  val ben = new Person("Ben", "Franklin")
  println(s"$ben was a great inventor.")
}

执行命令1

mkdir -p classes
scalac -d classes -classpath /tmp/example.jar UsePersonClass.scala

执行命令2

scala -classpath classes:/tmp/example.jar UsePersonClass

执行命令3

java -classpath $SCALA_HOME/lib/scala-library.jar:classes:/tmp/example.jar \
  UsePersonClass

运行结果

Ben Franklin was a great inventor.

14.2 在 Scala 中使用 Java 类

Intermixing/UseJDKClass.scala

import java.util.Currency

val currencies = Currency.getAvailableCurrencies
println(s"${currencies.size} currencies are available.")

执行命令

scala UseJDKClass.scala

运行结果

220 currencies are available.

Intermixing/java/InvestmentType.java

//Java code
package chapter14.usingjava;

public enum InvestmentType {
  BOND, STOCK, REAL_ESTATE, COMMODITIES, COLLECTIBLES, MUTUAL_FUNDS
}

运行结果

class investments.Investment

Intermixing/java/Investment.java

//Java code
package chapter14.usingjava;

public class Investment {                                        
  private String investmentName;
  private InvestmentType investmentType;
             
  public Investment(String name, InvestmentType type) {
    investmentName = name;
    investmentType = type;
  } 
  public int yield() { return 0; }
}

Intermixing/UseInvestment.scala

import chapter14.usingjava.{ Investment, InvestmentType }

object UseInvestment extends App {
  val investment = new Investment("XYZ Corporation", InvestmentType.STOCK)
  println(investment.getClass)
}

执行命令1

mkdir -p classes
javac -d classes java/InvestmentType.java java/Investment.java
scalac -classpath classes UseInvestment.scala
scala -classpath classes:. UseInvestment

执行命令2

java -classpath $SCALA_HOME/lib/scala-library.jar:classes:. UseInvestment

Intermixing/UseInvestmentError.scala

val theYield1 = investment.yield   //ERROR
val theYield2 = investment.yield() //ERROR

Intermixing/UseInvestmentYield.scala

val investment = new Investment("XYZ Corporation", InvestmentType.STOCK)
val theYield1 = investment.`yield`
val theYield2 = investment.`yield`()

14.3 在 Java 中使用 Scala 方法

Intermixing/Car.scala

package chapter14

class Car(val year: Int) {
  private[this] var miles: Int = 0

  def drive(distance: Int): Unit = { miles += distance }

  override def toString: String = s"year: $year miles: $miles"
}

Intermixing/UseCar.java

//Java code
package chapter14;

public class UseCar {
  public static void main(String[] args) {
    Car car = new Car(2009);
    
    System.out.println(car);
    car.drive(10);
    System.out.println(car);
  }
}

执行命令

mkdir -p classes
scalac -d classes Car.scala
javac -d classes -classpath $SCALA_HOME/lib/scala-library.jar:classes \
  UseCar.java
java -classpath $SCALA_HOME/lib/scala-library.jar:classes \
  automobiles.users.UseCar

14.4 在 Java 中使用特质

Intermixing/Writable.scala

trait Writable {
  def write(message: String): Unit
}

Intermixing/AWritableJavaClass.java

//Java code
public class AWritableJavaClass implements Writable {
  public void write(String message) {
    //...code...
  }
}

Intermixing/Printable.scala

trait Printable {
  def print(): Unit = {
    println("running printable...")
  }
}

执行命令1

mkdir -p classes
scalac -d classes Printable.scala

执行命令2

javap classes/Printable.class classes/Printable\$class.class

运行结果

Compiled from "Printable.scala"
public abstract class Printable$class {
  public static void print(Printable);
  public static void $init$(Printable);
}
Compiled from "Printable.scala"
public abstract class Printable$class {
  public static void print(Printable);
  public static void $init$(Printable);
}

Intermixing/APrintable.java

public class APrintable implements Printable {
  public void print() {
    System.out.println("We can reuse the trait here if we like...");
    //Printable$class.print(this); // for 2.11.x
    Printable.super.print(); // for 2.12.x
  }
  
  public static void use(Printable printable) {
    printable.print();
  }
  
  public static void main(String[] args) {
    APrintable aPrintable = new APrintable();
    use(aPrintable);
  }
}

执行命令

javac -d classes -classpath $SCALA_HOME/lib/scala-library.jar:classes \
  APrintable.java
java -classpath $SCALA_HOME/lib/scala-library.jar:classes APrintable

运行结果

We can reuse the trait here if we like...
running printable...

14.5 在 Java 中使用单例对象和伴生对象

Intermixing/Single.scala

package chapter14

object Single {
  def greet(): Unit = { println("Hello from Single") }
}

Intermixing/SingleUser.java

//Java code
public class SingleUser {
  public static void main(String[] args) {
    Single.greet();
  }
}

运行结果

Hello from Single

Intermixing/Buddy.scala

class Buddy {
  def greet(): Unit = { println("Hello from Buddy class") }
}

object Buddy {
  def greet(): Unit = { println("Hello from Buddy object") }
}

Intermixing/BuddyUser.scala

//Java code
public class BuddyUser {
  public static void main(String[] args) {
    new Buddy().greet();
    Buddy$.MODULE$.greet(); 
  }
}

运行结果

Hello from Buddy class
Hello from Buddy object

14.6 扩展类

Intermixing/BirdWithProblem.scala

abstract class BirdWithProblem {
  def fly(): Unit
  //...
}

Intermixing/Ostrich.scala

class Ostrich extends BirdWithProblem {
  override def fly(): Unit = {
    throw new NoFlyException
  }
  //...
}

Intermixing/NoFlyException.scala

class NoFlyException extends Exception {}

Intermixing/Penguin.java

//Java code
class Penguin extends Bird {      
  public void fly() throws NoFlyException {
    throw new NoFlyException();
  }
  //...
}
Penguin.java:3: error: fly() in Penguin cannot override fly() in Bird
  public void fly() throws NoFlyException {
              ^
  overridden method does not throw NoFlyException
1 error

Intermixing/Bird.scala

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