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 は、クラス階層がツリーであることを要求します。これは、その中の要素が 1 つだけの親クラスを持つことを意味します。多くの親のトレイトと多くの祖先クラスを持つことができますが、あなたが求めていることは、Scala が要求する階層に厳密に反しています。

さて、ここで 2 つの概念を混ぜ合わせています。何かの型がそのクラスまたは特性によって定義される名目上の型付けと、何かの型がそれが実装するメソッドによって定義される構造型型付けです。

ただし、名目上の型付けと構造的な型付けはかなり互換性がありません。実際、Scala が登場する前は、この 2 つは共存できないことが一般的に合意されていました。実際、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()

はい、A と B の代わりに Alice と Bob を使用する必要があります。ただし、私の場合は妥協できます。

于 2013-02-02T08:33:30.230 に答える