In my quest to expand my knowledge on Scala, I see some things that seem not possible due to generics constraints - gleaned from another question.
I have a contrived example. This works:
import scala.collection.mutable.ArrayBuffer
case class Z (a1:String, b1:String, id: Integer)
var d = new ArrayBuffer[Z]() // empty buffer is created
d = ArrayBuffer(Z("Mark", "Hamlin", 2), Z("Kumar", "XYZ", 3), Z("Tom", "Poolsoft", 4))
for (x <- d) {
val s = x.id
println(s)
}
Assume I have some common processing using generics - this does not work:
def someDef[T](x: ArrayBuffer[T]): Unit = {
for (a <- x) {
println("hello...")
val s = a.id // without this it works
println(s) // println(a) works
}
}
someDef(d)
Get this as error:
command-1621988840751517:18: error: value id is not a member of type parameter T
val s = a.id
^
What is the work around if this to be used for a set of classes represented by T in which we want part of the T to be accessed? I.e. generic processing - on those real generic aspects. val s = a.id
or some filtering? Seems restrictive. I get that there is a restriction, but how does one code around this?
CodePudding user response:
As is, you can have a T
that does not have an id
, such as someDef(new ArrayBuffer[Int]())
, so it is not type sound, that is why the type checker does not compile it. T
needs to be constrained by making Z
the upper bound of T
. Now T
always has id
:
def someDef[T <: Z](x: ArrayBuffer[T]): Unit = // ...
EDIT: If you want the id
value from T
and Z
was just an example, you can achieve that with an advanced type of polymorphism called structural types or colloquially known as duck typing. I'll let you read about it, and show you that in action:
def someDef[T <: { val id: Integer }](x: ArrayBuffer[T]): Unit = // ...
Now T
can be any type (subtype of AnyRef
), as long as it has a val id: Integer
defined:
case class Z(a1: String, b1: String, id: Integer)
val d = ArrayBuffer(Z("Mark", "Hamlin", 2), Z("Kumar", "XYZ", 3), Z("Tom", "Poolsoft", 4))
someDef(d)
case class A(whatever: String, id: Integer)
val d2 = ArrayBuffer(A("We", 1), A("are", 2), A("the", 3), A("champions", 4))
someDef(d2)