I have an issue with the following hierarchy in scala:
class ScalaGenericTest {
def getValue[A, B <: Abstract[A]](clazz: B): A = clazz.a
def call: String = {
val sub: Subclass = new Subclass
getValue(sub)
}
}
class Subclass extends Abstract[String] {
def a: String = "STRING"
}
abstract class Abstract[A] {
def a: A
}
The compiler doesn't seem to be able to bind the generic parameter A in the call to the getValue function -- I think it should be able to infer this from the definition of Subclass. The compile error is as follows:
inferred type arguments [Nothing,Subclass] do not conform to method getValue's type parameter bounds [A,B <: Abstract[A]]
It works if I explicitly pass the generic type arguments to the method, i.e. getValue[String,Subclass](sub)
but surely the compiler should be able to infer this?
The same hierarchy works fine in Java:
public class JavaGenericTest {
public <T,U extends Abstract<T>> T getValue(U subclass) {
return subclass.getT();
}
public String call(){
Subclass sub = new Subclass();
return getValue(sub);
}
private static class Subclass extends Abstract<String> {
String getT(){
return "STRING";
}
}
private static abstract class Abstract<T> {
abstract T getT();
}
}
I'm pretty new to Scala so there's probably some subtlety that I'm missing.
Thanks in advance for any help!
It's a limitation in Scala's type inference. The issue is described in SI-2272 (the example there uses implicits, but the same error occurs when using it explicitly). It's been closed as won't fix.
In that issue, Adriaan Moors advises to avoid constraints that have type variables on both sides. ie. B <: Abstract[A]
. An easy work-around would be to avoid the second type parameter altogether.
def getValue[A](clazz: Abstract[A]): A = clazz.a
scala> val sub = new Subclass
sub: Subclass = Subclass@585cbda6
scala> getValue(sub)
res11: String = STRING
Additionally, Adriaan also provided a way of using an implicit <:<
as another work around. To put it in the context of your example, it would look like:
def getValue[A, B](b: B)(implicit ev: B <:< Abstract[A]): B = b.a
Where an instance of <:<
is provided implicitly through Predef.
I have same problem at one time too. And created large implicit evidence hack to overcome. Afterwards I accidentally look into scala collection api docs and found a solution: http://www.scala-lang.org/api/2.11.4/index.html#scala.collection.generic.GenericTraversableTemplate
class ScalaGenericTest {
def getValue[A, B[X] <: Abstract[X]](clazz: B[A]): A = clazz.a
def call: String = {
val sub: Subclass = new Subclass
getValue(sub)
}
}
class Subclass extends Abstract[String] {
def a: String = "STRING"
}
abstract class Abstract[A] {
def a: A
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With