web-dev-qa-db-ja.com

暗黙のクラスは常にAnyValを拡張する必要がありますか?

拡張メソッドを書いているとしましょう

implicit class EnhancedFoo(foo: Foo) {
  def bar() { /* ... */ }
}

常に含める必要がありますextends AnyValクラス定義内?暗黙的なクラスを値クラスにしたくないのはどのような場合ですか?

57
Luigi Plinge

値のクラスにリストされている制限 を見て、それらが暗黙のクラスに適していない場合があることを考えてみましょう。

  1. 「タイプが値クラスではないpublic valパラメータを1つだけ持つプライマリコンストラクタのみが必要です。」したがって、ラップしているクラス自体が値クラスの場合、implicit classをラッパーとして使用することはできませんが、次のようにできます。

    // wrapped class
    class Meters(val value: Int) extends AnyVal { ... }
    
    // wrapper
    class RichMeters(val value: Int) extends AnyVal { ... }
    
    object RichMeters { 
      implicit def wrap(m: Meter) = new RichMeter(m.value)
    }
    

    ラッパーに暗黙のパラメーターもある場合は、それらをメソッド宣言に移動してみてください。つまりの代わりに

    implicit class RichFoo[T](foo: Foo[T])(implicit ord: Ordering[T]) {
      def bar(otherFoo: Foo[T]) = // something using ord
    }
    

    あなたが持っている

    implicit class RichFoo[T](foo: Foo[T]) extends AnyVal {
      def bar(otherFoo: Foo[T])(implicit ord: Ordering[T]) = // something using ord
    }
    
  2. 「特殊な型パラメーターがない可能性があります。」それ自体が特殊な型パラメーターを持つクラスをラップする場合、ラッパーを特殊化する必要がある場合があります。

  3. 「ネストされた、またはローカルのクラス、トレイト、またはオブジェクトが存在しない可能性があります」再度、ラッパーの実装に役立つ可能性があるものです。
  4. equalsまたはhashCodeメソッドを定義できません。」暗黙のクラスにもequals/hashCodeを含めないでください。
  5. 「トップレベルのクラスまたは静的にアクセス可能なオブジェクトのメンバーである必要があります」これは、通常、暗黙的なクラスを定義する場所でもありますが、必須ではありません。
  6. 「defsはメンバーとしてのみ持つことができます。特に、遅延vals、vars、またはvalsをメンバーとして持つことはできません。」 varsやlazy valsの賢明なユースケースは考えられませんが、暗黙的なクラスはそれらすべてを持つことができます。
  7. 「別のクラスによって拡張することはできません。」この場合も、暗黙のクラスを拡張できますが、その理由はおそらくありません。

さらに、暗黙クラスを値クラスにすると、リフレクションを使用してコードの動作が変更される可能性がありますが、リフレクションでは通常、暗黙クラスが表示されません。

暗黙のクラスがこれらの制限をすべて満たしている場合、それを値クラスにしない理由は考えられません。

48
Alexey Romanov

Value ClassesImplicit Classes を混同しているように思います。値クラスmustAnyValを拡張する一方で、拡張機能の暗黙クラスを定義する場合、ほとんど何も拡張しません。

0
Randall Schulz