6

Javaライブラリからの基本クラスがありますが、そのコードは変更できません。このクラス(A)には空のメソッド(b)があり、代わりに抽象として宣言する必要があります。

class A {
  def b { }
}

このクラスをScalaで拡張し、メソッドをオーバーライドして抽象化します。

abstract class AA extends A {
  override def b
}

今、私はこのメソッドをトレイトに実装します:

trait B {
  def b { println("B") }
}

特性BでAAを拡張すると、エラーが発生します。タイプ=>ユニットのクラスAのメソッドbをオーバーライドします。タイプ=>の特性Bのメソッドbには、「オーバーライド」修飾子が必要です。

class C extends AA with B {}

代わりに、コードがこのようであった場合、すべてがエラーなしでコンパイルされます。これは私には少し矛盾しているようです。

abstract class AA {
  def b
}

trait B {
  def b { println("B") }
}

class C extends AA with B {}

私はScala2.8.0RC3を実行していて、この言語にまったく慣れていません(3日)。もう1つの奇妙で関連する動作は、bを抽象化するときにオーバーライドラベルが不要なことです。

abstract class AA extends A {
  def b
}
4

3 に答える 3

6

何が起こっているのかを確認するために、次のことを試しました。

scala> class A{
     |   def b{ }
     | }
defined class A

scala> abstract class AA extends A{
     |   override def b
     | }
defined class AA

scala> class AAA extends AA{
     |   def b = println("AAA")
     | }
<console>:8: error: overriding method b in class A of type => Unit;
 method b needs `override' modifier
         def b = println("AAA")
             ^

明らかに、問題の原因は、抽象クラスのサブクラスに「オーバーライド」修飾子を含める必要性から、抽象クラスがスーパークラスのメソッドを「解放」できないことです。

于 2010-07-04T11:52:49.337 に答える
2

これが正しい解決策かどうかはわかりませんが、trait B拡張A(およびオーバーライドb) している場合は、すべて正常にコンパイルされます。

まず、定義して、質問に提示するようにしましょAAA

C:\Users\VonC>scala
Welcome to Scala version 2.8.0.RC5 (Java HotSpot(TM) 64-Bit Server VM, Java 1.6.0_18).
Type in expressions to have them evaluated.
Type :help for more information.

scala> class A {
     | def b { println("A b") }
     | }
defined class A

scala> new A
res5: A = A@153bedc4

scala> res5.b
A b

scala> abstract class AA extends A {
     | override def b
     | }
defined class AA

あなたがしたこと:

scala> trait B {
     | override def b { println("B b") }
     | }
<console>:6: error: method b overrides nothing
       override def b { println("B b") }
                    ^

特性 B で試したこと (「override」を追加できるようにするため):

scala> trait B extends A {
     | override def b { println("B b") }
     | }
defined trait B

だから今:

scala> class C extends AA with B {}
defined class C

scala> new C
res7: C = C@1497b7b1

scala> res7.b
B b

正しいbオーバーライドされたメソッドが呼び出されますC.b


明らかな「矛盾」については、Scala for Java Refugees Part 5: Traits and Types を参照してください。

まず、いつも悩ましいoverrideキーワードがあります。基本的な OOP に関する記事で、スーパークラスのメソッドをオーバーライドするメソッドはオーバーライド修飾子を使用して宣言する必要があると述べました。当時、私はそれを@Overrideアノテーションの使用を義務付ける言語になぞらえ、その主な目的はグッド プラクティスを強制することでした。

トレイトの力の本当の鍵は、コンパイラが継承クラスでそれらを処理する方法です。
特性は実際にはミックスインであり、真の親クラスではありません
非抽象的特性メンバーは、物理的にクラスの一部として、実際には継承クラスに含まれます。まあ、物理的にではありませんが、あなたは写真を手に入れます。
これは、コンパイラが非抽象メンバーでカット アンド ペーストを実行し、それらを継承クラスに挿入するかのようです。これは、継承パスにあいまいさがなく、ひし形の問題がないことを意味します。

したがってoverride、2 番目の例ではキーワードは必要ありません。

于 2010-07-04T11:28:59.823 に答える
2

問題は非常に微妙です。経験則として、Aを拡張するクラスAAは、 Aも拡張する特性と混合する必要があります。

あなたがした:

class A {
  def b { }
}

abstract class AA extends A {
  override def b
}

trait B {
  def b { println("B") }
}

したがって、AABを混在させると、メソッドbが 2 回定義されます。1 回はA ( Bの定義がAAのオーバーライドを置き換えたためオーバーライドされません) で、2 回目はBで、コンパイラは 2 つの (同じ名前であるが関連のない) メソッドの間に階層がないため、どちらか一方を選択できません。必要に応じて、次のように考えてください。コンパイラはAABの本体を「混合」します。彼がAAからメソッドを選択した場合、それは抽象的になり、Bからメソッドを選択した場合(何が起こったのか)、オーバーライドではないため、2つのメソッドbで立ち往生します.

これを解決するには、両方のメソッドoverrideが同じメソッドであることを確認する必要があります。この場合、コンパイラは、同じメソッドについて話していることを理解し、最後に混合されたトレイトを優先します。

ここで、Bのメソッドbをオーバーライドするには、そのクラスもAから継承する必要があります。したがって、これを行う標準的な方法は次のようになります。

class A {
  def b { }
}

abstract class AA extends A {
  override def b
}

trait B extends A{
  def b { println("B") }
}

class C extends AA with B {}

これは問題なくコンパイルされます。

今、あなたがするとき:

abstract class AA {
  def b
}

trait B {
  def b { println("B") }
}

class C extends AA with B {}

両方のメソッドが同じであることは明らかなので、コンパイラはトレイトのメソッドを使用する必要があることを認識しています。

その他のソリューションは次のとおりです。

  1. BをAAにオーバーライドさせる
  2. bA の抽象化にします (しかし、あなたはそれを望んでいませんでした)

繰り返しますが、問題は非常に微妙ですが、もう少し明確にしていただければ幸いです。理解を深めるには、Scala の Stackable Trait Patternをお読みください。

于 2010-07-05T12:51:56.527 に答える