(注:明白な理由のためにタイトルで「問題」の代わりに「エラー」を使用しました。;))。
ScalaでTraitsの基本的な読書をしました。これらはJavaまたはC#のインターフェイスに似ていますが、メソッドのデフォルト実装を許可します。
私は不思議に思っていました:これは「ダイヤモンドの問題」のケースを引き起こすことはできません。それが多くの言語がそもそも多重継承を避ける理由です。
その場合、Scalaはこれをどのように処理しますか?
(注:明白な理由のためにタイトルで「問題」の代わりに「エラー」を使用しました。;))。
ScalaでTraitsの基本的な読書をしました。これらはJavaまたはC#のインターフェイスに似ていますが、メソッドのデフォルト実装を許可します。
私は不思議に思っていました:これは「ダイヤモンドの問題」のケースを引き起こすことはできません。それが多くの言語がそもそも多重継承を避ける理由です。
その場合、Scalaはこれをどのように処理しますか?
回答:
ダイヤモンドの問題は、選択するメソッドの実装を決定できないことです。Scalaは、言語仕様の一部として選択する実装を定義することでこれを解決します(このウィキペディアの記事でScalaに関する部分を読んでください)。
もちろん、同じ順序定義をクラスの多重継承で使用することもできますが、なぜ特性に煩わされるのでしょうか?
IMOがコンストラクターである理由。コンストラクターには、通常のメソッドにはないいくつかの制限があります-それらはオブジェクトごとに1回しか呼び出すことができず、新しいオブジェクトごとに呼び出す必要があり、子クラスのコンストラクターは最初の命令として親のコンストラクターを呼び出す必要があります(ほとんどの言語はパラメータを渡す必要がない場合は、暗黙的に行います)。
BとCがAとDを継承し、BとCの両方のコンストラクターがAのコンストラクターを呼び出す場合、DのコンストラクターはAのコンストラクターを2回呼び出します。Scalaがメソッドで行ったように選択する実装を定義することは、 BとCの両方のコンストラクターを呼び出す必要があるため、ここでは機能しません。
トレイトにはコンストラクターがないため、この問題を回避できます。
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」と呼ばれることがあります。