Type parameters aren't your problem here, the problem is that ClassA
tries to mix in three copies of commonFunction()
, which differ only by return type:
class ClassA extends TraitB with TraitC {
def commonFunction() : ClassA = { /// some code }
def commonFunction() : ClassB = { /// some code }
def commonFunction() : ClassC = { /// some code }
}
Although the JVM does allow overloading on return type, it isn't permitted at compile time - there's just too much chance for confusion (especially if type inference is involved).
The solution is often to use f-bounded polymorphism (as you did with commonUpdateFunction()
), but it's impossible to show how to do that here given that all your commonFunction()
definitions are concrete.
It would help a lot to see more "real" code!
UPDATE: Based on new information from the comments.
Instead of a type parameter you might find life easier with an abstract type member. Using Repr
(for "Repr"esentation) is a common enough convention and used in the collections lib; make sure that this abstract type member has a bound!
Stick the other common attributes in here as well:
trait Employee {
type Repr <: Employee
def name : String
def id : Int
def withName(name: String) : Repr
def withId(id: Int) : Repr
}
sub-traits follow a similar pattern. There's no need to re-declare other abstract members that keep their signature. You can also introduce other members as you refine the types here.
trait ManagingEmployee extends Employee {
type Repr <: ManagingEmployee
def numberOfReports: Int
def withNumberOfReports(x: Int) : Repr
}
trait SkilledEmployee extends Employee {
type Repr <: SkilledEmployee
def skill: String
}
Now make the leaf nodes of our type tree concrete. case classes work well here, though there will sadly be some duplication (macros could possibly help, but that's a different question).
Note how name
and id
are made concrete by the class params, the Repr
type is made explicit via the =
sign, and the abstract methods have to be explicitly re-defined in each leaf class:
case class HrManager(
name : String,
id : Int,
numberOfReports : Int
) extends ManagingEmployee {
type Repr = HrManager
def withName(name: String) = this.copy(name = name)
def withId(id: Int) = this.copy(id = id)
def withNumberOfReports(x: Int) = this.copy(numberOfReports = id)
}
case class Technician(name: String, id: Int) extends SkilledEmployee {
type Repr = Technician
def withName(name: String) = this.copy(name = name)
def withId(id: Int) = this.copy(id = id)
val skill = "programming"
}
case class TechnicalManager(
name : String,
id : Int,
numberOfReports : Int
) extends SkilledEmployee with ManagingEmployee {
type Repr = TechnicalManager
def withName(name: String) = this.copy(name = name)
def withId(id: Int) = this.copy(id = id)
def withNumberOfReports(x: Int) = this.copy(numberOfReports = id)
val skill = "software architecture"
}