web-dev-qa-db-ja.com

Scalaのトレイトは「ダイヤモンドエラー」をどのように回避しますか?

(注:明らかな理由で、タイトルに「問題」ではなく「エラー」を使用しました。;))

Scalaの特性に関する基本的な読み物をしました。これらは、JavaまたはC#のインターフェイスに似ていますが、メソッドのデフォルト実装を可能にします。

私は疑問に思っていました:これが「ダイヤモンドの問題」の原因となるのではないでしょうか。

もしそうなら、Scalaはこれをどのように処理しますか?

16
Aviv Cohn

ひし形の問題は、選択するメソッドの実装を決定できないことです。 Scalaは、言語仕様の一部として選択する実装を定義することによってこれを解決します( Scalaに関する部分を読んでください )。

もちろん、同じ順序定義をクラスの多重継承でも​​使用できるので、なぜトレイトに悩むのでしょうか?

IMOがコンストラクターである理由。コンストラクターには、通常のメソッドにはないいくつかの制限があります-それらはオブジェクトごとに1回だけ呼び出すことができ、新しいオブジェクトごとに呼び出す必要があります。子クラスのコンストラクターは、最初の命令として親のコンストラクターを呼び出す必要があります(ほとんどの言語はパラメータを渡す必要がない場合は、暗黙的に行います)。

BとCがAを継承し、DがBとCを継承し、BとCの両方のコンストラクターがAのコンストラクターを呼び出す場合、DのコンストラクターはAのコンストラクターを2回呼び出します。 Scalaメソッドで行った実装の定義は、ここでは機能しません(bothBとCのコンストラクタは呼ばれた。

トレイトはコンストラクタを持たないため、この問題を回避します。

22
Idan Arye

Scalaは「特性の線形化」と呼ばれるものによってダイヤモンドの問題を回避します。基本的には、右から左に拡張する特性でメソッド実装を検索します。簡単な例:

trait Base {
   def op: String
}

trait Foo extends Base {
   override def op = "foo"
}

trait Bar extends Base {
   override def op = "bar"
}

class A extends Foo with Bar
class B extends Bar with Foo

(new A).op
// res0: String = bar

(new B).op
// res1: String = foo

そうは言っても、検索する特性のリストには、他の特性を拡張する可能性があるため、明示的に指定した特性よりも多くのものが含まれている可能性があります。詳細な説明はここにあります: スタック可能な変更としての特性 と線形化のより完全な例はここにあります: なぜ多重継承ではないのですか?

他のプログラミング言語では、この動作は「メソッド解決順序」または「MRO」と呼ばれることがあります。

21
lutzh