Search code examples
scalainheritancetraitsbase-class

Mixing trait and base class with same method


Can I safely inherit both a trait and a base class which share the same method?

Like:

trait MyTrait {
   def getValue:String
}

class SomeClass {
   def getValue:String = [concrete implementation]
}

then:

class MyClass extends SomeClass with MyTrait

The reason to do this is I'd like to use it in another (abstract) class, which defines the required API only via the MyTrait trait, and I cannot have SomeClass inherit this trait directly (because its from another unrelated library).

abstract class AbstractOtherClass {
   val mainObject:MyTrait
}

and then the concrete implementation of that class:

class OtherClass extends AbstractOtherClass {
   val mainObject:MyTrait = new MyClass

   def something = {
      println(mainObject.getValue)  // shall call SomeClass.getValue
   }
}

Solution
After SergGr's suggestion I ended up with a solution like this:

abstract class AbstractOtherClass {
   def getValue:String
}

class OtherClass extends AbstractOtherClass {
   val mainObject:SomeClass = ...

   def getValue:String = mainObject.getValue
}

Solution

  • It looks like there are two different questions here

    1. Does this work merely by an accident?
    2. It this a good design?

    As long as your trait has only declaration of the method but no implementation, it works by design. This is one of the unusual but expected usage pattern. If several traits or a trait and a class have implementations of the same method, there is still an explicitly specified logic of linearization that specifies how to resolve the conflicts but I'd say that in most cases relying on that is a bad design.

    The thing you are trying to do looks like an adapter pattern to me and I'd say that from the design point of view it is better to implement it using object composition rather than inheritance:

    trait MyTrait {
       def getValue:String
    }
    
    class SomeClassWrapper(delegate: SomeClass) extends MyTrait {
        override def getValue:String = delegate.getValue       
    }
    

    Doing it this way you don't bind your API to the implementation of the library. For example you can rename the method to getAbcValue value if this makes more sense in your context. Or if you at some point find another library that does the same work better but that has a different name for such method (like calculateValue), you have to create just another wrapper class and don't have to change all the getValue calls to calculateValue calls.