11.4 继承类

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

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

  1. abstract class Bird {
  2. def fly();
  3. //...
  4. }

还有另一个类Ostrich

WorkingWithScriptsAndClasses/Ostrich.scala

  1. class Ostrich extends Bird {
  2. def fly() {
  3. throw new NoFlyException
  4. }
  5. //...
  6. }

其中NoFlyException定义如下:

WorkingWithScriptsAndClasses/NoFlyException.scala

  1. class NoFlyException extends Exception {}

在上面的代码里,Ostrichfly()抛出异常没有任何问题。不过,如果要在Java里实现一个不能飞的鸟,就会有麻烦,如下所示:

WorkingWithScriptsAndClasses/Penguin.java

  1. //Java code
  2. class Penguin extends Bird {
  3. public void fly() throws NoFlyException {
  4. throw new NoFlyException();
  5. }
  6. //...
  7. }

首先,如果只是抛出异常,Java会报错“unreported exception NoFlyException; must be caught or declared to be thrown.”一旦加上了throws子句,Java又会报错“fly() in Penguin cannot override fly() in Bird; overridden method does not throw NoFlyException.”

即便Scala很灵活,并不强求一定要指定抛出哪些异常,但是要想在Java里继承这些方法,就要告诉Scala编译器,把这些细节记录在方法签名里。Scala为此提供了一个后门:定义@throws注解。

虽然Scala支持注解,但它却不提供创建注解的语法。如果想创建自己的注解,就不得不用Java来做。@throws是已经提供好的注解,用以表示方法抛出的受控异常。这样,对我们来说,要在Java里实现Penguin,必须在把Bird改成这样:

WorkingWithScriptsAndClasses/Bird.scala

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

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

我们已经看到了,Java和Scala互操作是多么容易。对于一致的构造,感觉就像使用其他Java类一样。我们还学会了如何使用“只在一种语言里支持,在另一种语言里不支持”的构造。Scala的一个关键优势在于,它支持Java的语义,并可以用函数式风格将其进一步扩展。与企业级应用和旧代码打交道时,不必舍弃之前的代码。在应用程序里,可以轻松地把Scala代码和既有的Java代码混合到一起。