2017-05-18 2 views
1

に抽象ヴァルの適切後半に初期化を作成し、以下の場合考えてみましょう。それは別の無関係なクラスによって使用されているようトレイト

trait A { 
    protected val mydata = ??? 

    def f(args) = ??? //uses mydata 
} 

class B 
class C 

class D(arg1: String) extends B with A { 
    override val mydata = ??? /// some calculation based on arg1 
} 

class E(arg1: String) extends C with A{ 
    override val mydata = ??? /// some calculation based on arg1 
} 

をAが特色である必要があります。問題は、mydataの定義を実装する方法です。

標準の方法(多くの場所では、mydataをdefで定義して子でオーバーライドすることが推奨されていますが、fがmydataを変更しないと仮定した場合、 。代わりに、valを持つの

もう一つの方法は何をするだろう:

trait A { 
    protected val mydata = g 
    protected def g() 
} 

(別の機能を追加することを越えて)これに伴う問題は、gが子に建設変数に依存している場合、これらはメンバーにならなければならないということです(例えば、データが大きく、建設中に与えられた場合には問題となる可能性がある) :私は抽象としてトレイトでのvalを残す場合は

class D(arg1: Seq[String]) { 
    def g() = ??? // some operation on arg1 
} 

I)は、このようなhere見られるような問題点に到達することができます。

私が探しているのは、子供のvalの値を定義し、それがvalであり、遅い計算のためにデータを保存する必要がないことを保証する方法です。 Javaでどのように似た何か私は最終的なvalを定義し、コンストラクタでそれを埋めることができます

答えて

1

標準的な方法は、多くの場所でmydataをdefとして定義し、私は抽象的な私はここに発見されたものなどの問題に達することができるように形質のヴァルを残します)。

これは一般的な誤解であり、リンクされた質問への回答も示されています。問題はvalとして実装していますが、いずれにしても必要です。コンクリートvalをオーバーライドすると悪化します。抽象的なものは少なくともlazy valで実装できます。問題を回避する唯一の方法は、がAのコンストラクタまたはそのサブタイプに、直接的または間接的に初期化されるまでアクセスされないようにすることです。 fでの使用は安全です(fがコンストラクタで呼び出されない場合は、mydataへの間接的なアクセスです)。

あなたはこの要件を確保することができた場合は、

trait A { 
    protected val mydata 
    def f(args) = ??? //uses mydata 
} 

class D(arg1: String) extends B with A { 
    override val mydata = ??? /// some calculation based on arg1 
} 

class E(arg1: String) extends C with A{ 
    override val mydata = ??? /// some calculation based on arg1 
} 

はまさに正しい定義です。

あなたができない場合は、欠点にもかかわらず最後の解決策を取り入れる必要がありますが、同様の初期化順序の問題を回避するためにはをlazyにする必要があります。