私は効果的なscalaスライドを見ていましたが、スライド10で抽象メンバーval
には決して使用せず、代わりに使用することについて言及しています。このスライドでは、abstractを aで使用することがアンチパターンである理由について詳しく説明していません。誰かが抽象メソッドのトレイトで val と def を使用することに関するベストプラクティスを説明できれば幸いです trait
def
val
trait
4 に答える
は、 、、 、または のいずれ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 に答えてください。abstract を使用val
すると、初期化の問題が発生する可能性があります。
trait Foo {
val bar: Int
val schoko = bar + bar
}
object Fail extends Foo {
val bar = 33
}
Fail.schoko // zero!!
これは醜い問題であり、私の個人的な意見では、将来の Scala バージョンではコンパイラーで修正することで解消されるはずですが、そうです、現在これは、abstract を使用すべきではない理由でもありますval
。
編集(2016 年 1 月): 抽象val
宣言をlazy val
実装でオーバーライドすることが許可されているため、初期化の失敗も防ぐことができます。
val
val 宣言の初期化順序が不明確で直感的でないため、トレイトでは使用しないことを好みます。すでに機能している階層に特性を追加すると、以前に機能していたすべてのものを壊す可能性があります。私のトピックを参照してください:非最終クラスで単純な 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 仮想マシンは、同じ型が返されることを期待しています。