Should implicit classes always extend AnyVal?

Luigi Plinge picture Luigi Plinge · Feb 18, 2013 · Viewed 10.5k times · Source

Say I'm writing an extension method

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

Should you always include extends AnyVal in the class defininition? Under what circumstances would you not want to make an implicit class a value class?

Answer

Alexey Romanov picture Alexey Romanov · Feb 18, 2013

Let's look at the limitations listed for value classes and think when they may not be suitable for implicit classes:

  1. "must have only a primary constructor with exactly one public, val parameter whose type is not a value class." So if the class you are wrapping is itself a value class, you can't use an implicit class as a wrapper, but you can do this:

    // 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)
    }
    

    If your wrapper has implicit parameters as well, you can try to move them to the method declarations. I.e. instead of

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

    you have

    implicit class RichFoo[T](foo: Foo[T]) extends AnyVal {
      def bar(otherFoo: Foo[T])(implicit ord: Ordering[T]) = // something using ord
    }
    
  2. "may not have specialized type parameters." You may want the wrapper to be specialized when wrapping a class which itself has specialized type parameters.

  3. "may not have nested or local classes, traits, or objects" Again, something which may well be useful for implementing a wrapper.
  4. "may not define a equals or hashCode method." Irrelevant, since implicit classes also shouldn't have equals/hashCode.
  5. "must be a top-level class or a member of a statically accessible object" This is also where you'd normally define implicit classes, but not required.
  6. "can only have defs as members. In particular, it cannot have lazy vals, vars, or vals as members." Implicit classes can have all of those, though I can't think of a sensible usecase for vars or lazy vals.
  7. "cannot be extended by another class." Again, implicit classes can be extended, but there is probably no good reason to.

In addition, making your implicit class a value class could possibly change some behavior of code using reflection, but reflection shouldn't normally see implicit classes.

If your implicit class does satisfy all of those limitations, I can't think of a reason not to make it a value class.