I have some class hierarchy:
trait Params
trait ParamsWithName extends Params {
val name: String
}
case class ParamsWithNameAndValue(name: String, value: String) extends ParamsWithName
I want to implement a few classes that will use these. The following works:
trait Worker[T <: ParamsWithName] {
def work(parameters: T): String = parameters.name
}
class SimpleWorker extends Worker[ParamsWithNameAndValue] {
override def work(parameters: ParamsWithNameAndValue): String = s"${parameters.name} ${parameters.value}"
}
The following does not:
trait Worker {
def work[T <: ParamsWithName](parameters: T): String = parameters.name
}
class SimpleWorker extends Worker {
override def work[ParamsWithNameAndValue](parameters: ParamsWithNameAndValue): String = s"${parameters.name} ${parameters.value}"
}
The error is:
error: value name is not a member of type parameter ParamsWithNameAndValue
override def work[ParamsWithNameAndValue](parameters: ParamsWithNameAndValue): String = s"${parameters.name} ${parameters.value}"
error: value value is not a member of type parameter ParamsWithNameAndValue
override def work[ParamsWithNameAndValue](parameters: ParamsWithNameAndValue): String = s"${parameters.name} ${parameters.value}"
I would like to understand why this is the case.
Scala version is 2.12.
CodePudding user response:
This:
trait Worker[T <: ParamsWithName] {
def work(parameters: T): String = parameters.name
}
Means that the Worker type itself is parametric on T (which must be a subtype of ParamsWithName). Which implies that a concrete instance of Worker will only work for one such concrete type for T
Thus, here:
class SimpleWorker extends Worker[ParamsWithNameAndValue] {
You are saying that the SimpleWorker type is a subtype of Worker[ParamsWithNameAndValue] meaning that all instances of SimpleWorker are instances of Worker[ParamsWithNameAndValue]
Whereas, this:
trait Worker {
def work[T <: ParamsWithName](parameters: T): String = parameters.name
}
Means that any instance of Worker has a method work which is parametric on T (which must be a subtype of ParamsWithName). Implying that any Worker must be able to handle any possible type T
Thus, here:
class SimpleWorker extends Worker {
override def work[ParamsWithNameAndValue](parameters: ParamsWithNameAndValue): String = s"${parameters.name} ${parameters.value}"
}
You are not only having a syntax error, but a conceptual problem.
Because you want to say that the work method on SimpleWorker can only accept ParamsWithNameAndValue but that would be a violation of the Liskov substitution principle.
In conclusion, the first approach is the one that encodes the intention you want.
