私は効果的なスカラスライドを調べていましたが、スライド10には、抽象メンバーvalには絶対にtrait使用せdefず、代わりに使用するように記載されています。スライドではval、atraitでabstractを使用することがアンチパターンである理由については詳しく説明していません。誰かが抽象メソッドの特性でvalvsdefを使用することに関するベストプラクティスを説明できれば幸いです
私は効果的なスカラスライドを調べていましたが、スライド10には、抽象メンバーvalには絶対にtrait使用せdefず、代わりに使用するように記載されています。スライドではval、atraitでabstractを使用することがアンチパターンである理由については詳しく説明していません。誰かが抽象メソッドの特性でvalvsdefを使用することに関するベストプラクティスを説明できれば幸いです
回答:
Aは、defのいずれかによって実施することができるdef、val、lazy valまたはobject。つまり、これはメンバーを定義する最も抽象的な形式です。形質は言って、通常は抽象インターフェイスですので、あなたがしたいvalと言っているどのような実装を行う必要があります。を要求したval場合、実装クラスはを使用できませんdef。
Avalは、パス依存型など、安定した識別子が必要な場合にのみ必要です。それはあなたが通常必要としないものです。
比較:
trait Foo { def bar: Int }
object F1 extends Foo { def bar = util.Random.nextInt(33) } // ok
class F2(val bar: Int) extends Foo // ok
object F3 extends Foo {
lazy val bar = { // ok
Thread.sleep(5000) // really heavy number crunching
42
}
}
あなたが持っていた場合
trait Foo { val bar: Int }
F1またはを定義することはできませんF3。
わかりました。混乱して@ om-nom-nomに答えると、抽象valsを使用すると、初期化の問題が発生する可能性があります。
trait Foo {
val bar: Int
val schoko = bar + bar
}
object Fail extends Foo {
val bar = 33
}
Fail.schoko // zero!!
これは醜い問題であり、私の個人的な意見では、コンパイラで修正することで将来のScalaバージョンで解消されるはずですが、現在、これは抽象valsを使用すべきでない理由でもあります。
編集(2016年1月):抽象val宣言をlazy val実装でオーバーライドできるため、初期化の失敗も防止できます。
valをでオーバーライドできますlazy val。だっF3たら作成できないというあなたの主張は正しくありません。とは言うbarvaldef
val schoko = bar + barと、Foo / Failの例は期待どおりに機能しますlazy val schoko = bar + bar。これは、初期化の順序をある程度制御する1つの方法です。また、派生クラスでlazy val代わりにを使用するとdef、再計算が回避されます。
val bar: Intしdef bar: Int Fail.schokoてもまだゼロです。
valval宣言には不明確で直感的でない初期化の順序があるため、トレイトで使用することは好みません。すでに機能している階層にトレイトを追加すると、以前は機能していたすべてのものが壊れてしまいます。私のトピックを参照してください:非最終クラスでプレーンvalを使用する理由
このval宣言の使用に関するすべてのことを念頭に置いておく必要があります。これにより、最終的にエラーが発生します。
ただし、の使用を避けられない場合がありますval。@ 0__が言及したように、安定した識別子が必要な場合がありますが、そうでdefはありません。
彼が何について話していたかを示す例を提供します。
trait Holder {
type Inner
val init : Inner
}
class Access(val holder : Holder) {
val access : holder.Inner =
holder.init
}
trait Access2 {
def holder : Holder
def access : holder.Inner =
holder.init
}
このコードはエラーを生成します:
StableIdentifier.scala:14: error: stable identifier required, but Access2.this.holder found.
def access : holder.Inner =
少し時間をとって考えると、コンパイラには文句を言う理由があることが理解できます。Access2.accessどうしても戻り値の型を導き出せなかった場合。def holder幅広い方法で実装できることを意味します。通話ごとに異なるホルダーを返す可能性があり、ホルダーには異なるInnerタイプが組み込まれます。ただし、Java仮想マシンは、同じタイプが返されることを想定しています。
このようなものは機能しないため、常にdefを使用するのは少し厄介なようです。
trait Entity { def id:Int}
object Table {
def create(e:Entity) = {e.id = 1 }
}
次のエラーが発生します。
error: value id_= is not a member of Entity
var。重要なのは、それらがフィールドである場合、そのように指定する必要があるということです。私はただdef近視眼的なものとしてすべてを持っていると思います。
varカプセル化を破りましょう。ただし、def(またはval)を使用することは、グローバル変数よりも優先されます。あなたが探しているのは、case class ConcreteEntity(override val id: Int) extends Entityそこから作成できるようなものだと思います。def create(e: Entity) = ConcreteEntity(1)これは、カプセル化を解除して任意のクラスにEntityの変更を許可するよりも安全です。