Search code examples
scalaimplicits

Avoiding explicit delegation when using implicits to implement a Trait


I am using several classes like this:

class MyClassOne {
  doSomething(a : A) : B
  ... 
} 

class MyClassTwo {
  doSomething(a : A) : B
  ... 
} 

The source for the classes is not under my control. Although the classes have methods with the same signature they don't implement an interface. I would like to define methods that work on all the classes.

So I've made a Trait like this:

trait MyTrait {
  doSomething(a : A) : B
}

And have made implicit conversions for all the classes:

implicit class MyClassOneMyTrait(myClassOne : MyClassOne) {
  def doSomething(a : A) = myClassOne.doSomething(a)
}

Is there a way to take advantage of the naming conventions used in the classes and avoid explicitly delegating to the objects passed into the constructor? More generally, is using a Trait with implicit classes the best approach to this problem?


Solution

  • Actually, there is scala feature exactly for your situation. It's called structural types.

    For structural types type requirements are expressed by interface structure instead of a concrete type. Internal implementation uses reflection, so be performance-aware!
    type A = String
    type B = String
    
    class MyClassOne {
      def doSomething(a: A): B = {
        println("1")
        "1"
      }
    }
    
    class MyClassTwo {
      def doSomething(a: A): B = {
        println("2")
        "2"
      }
    }
    
    type MyClassType = {
      def doSomething(a: A): B
    }
    
    List[MyClassType](new MyClassOne, new MyClassTwo).foreach(_.doSomething("test"))
    List(new MyClassOne, new MyClassTwo).foreach {
      case a: MyClassType => a.doSomething("test")
      case _ =>
    }