Scala:继承traits中的匿名类型

时间:2013-02-02 03:16:30

标签: scala types traits

在Scala中,假设类A和B具有通用方法,但没有继承任何公共接口:

class A {
  def foo() {
    println("A.foo")
  }
}

class B {
  def foo() {
    println("B.foo")
  }
}

我想要做的是定义一个特性,扩展任何具有foo()方法的类。我试过这个:

type Fooable = {
  def foo()
}

trait Q extends Fooable {
  override def foo() {
    println("before super.foo")
    super.foo()
    println("after super.foo")
  }
}

(new A with Q).foo()
(new B with Q).foo()    

但是,Scala编译器拒绝此代码时出现错误:

error: class type required but AnyRef{def foo(): Unit} found trait Q extends Fooable {

最好的解决方案是创建一个包含常用方法的接口,并修改类A和B以继承接口。不幸的是,我们无法修改这些类。这在Java库中很常见,例如:

  • java.sql.Connection.close()java.io.Closeable.close()
  • android.app.Activity.onDestroy()android.app.Service.onDestroy()

2 个答案:

答案 0 :(得分:3)

你不能这样做。这是它的根本问题,引用你:

  

定义一个扩展任何类的特征

Scala要求 class 层次结构为树,这意味着其中的任何元素都将只有一个父类。它可以有许多父特征和许多祖先类,但是你要求的是严格违反Scala要求的层次结构。

现在,你在这里混合两个概念:名义类型,其中某种类型由其类或特征定义,以及结构类型,其中某种类型的东西是由什么方法定义的实现

然而,名义打字和结构打字相当不相容。事实上,在Scala之前,人们普遍认为两者不能同居,而事实上,Scala的结构打字版本非常有限。你可以这样做:

scala> def f(m: { def foo(): Unit }) = {
     |     println("before foo")
     |     m.foo()
     |     println("after foo")
     | }
f: (m: AnyRef{def foo(): Unit})Unit

scala> f(new A)
before foo
A.foo
after foo

或者你可以将结构类型与自我类型结合起来,如下所示:

scala> trait Q { self: { def foo(): Unit } =>
     |   def bar() {
     |     println("before foo")
     |     foo()
     |     println("after foo")
     |   }
     | }
defined trait Q

scala> (new A with Q).bar()
before foo
A.foo
after foo

scala> (new B with Q).bar()
before foo
B.foo
after foo

但是你不能从结构类型继承,因为继承是一种不同于名义类型的特性。并且,因为您无法继承结构类型,所以不能覆盖它,或者在祖先上调用它,这意味着trait Q无法在foo()上声明覆盖。

答案 1 :(得分:0)

自我回答:

在我知道我的问题没有确切的解决方案后,我提出了另一种方法来克服这个问题。 它不是一个完美的解决方案,但在某些情况下可能是一个合理的解决方法(在我的情况下: - )。

trait Fooable {
  def beforeFoo() {}
  def afterFoo() {}
}
trait Alice extends A with Fooable {
  override def foo() {
    beforeFoo()
    super.foo()
    afterFoo()
  }
}
trait Bob extends B with Fooable {
  override def foo() {
    beforeFoo()
    super.foo()
    afterFoo()
  }
}
trait Quest extends Fooable {
  override def beforeFoo() {
    println("before super.foo")
    super.beforeFoo()
  }

  override def afterFoo() {
    println("after super.foo")
    super.afterFoo()
  }
}

(new Alice with Quest).foo()
(new Bob with Quest).foo()

是的,我们应该使用Alice和Bob,而不是A和B.但是,在我的情况下,它可能会受到损害。