2

在 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()
4

2 回答 2

4

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

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

Scala 要求层次结构是一棵树,这意味着其中的任何元素都只有一个父类。它可以有许多父特征和许多祖先类,但您所要求的严格违反 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().

于 2013-02-02T03:50:51.893 回答
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。但是,在我的情况下,它可能会受到影响。

于 2013-02-02T08:33:30.230 回答