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.