When using path-dependent types with reflection I am getting a type mismatch error even though I have matching "underlying types". What are these "non-underlying types" and why are they checked instead of the "underlying types"?
In the code below, I want the compare
method to accept only same-type subclasses of A as arguments. The error is on the last line.
abstract class A(val a:Int) {
type Impl <: A
def compare(other:Impl) {
if(a==other.a) println("equal") else println("diff")
}
}
class B(a:Int) extends A(a) {type Impl = B}
object Test {
def newInst(a: Int, className: String) = {
val constr = Class.forName(className).getConstructors()(0)
constr.newInstance(a.asInstanceOf[AnyRef]).asInstanceOf[A]
}
def main(args: Array[String]) {
val b1 = newInst(4, "B")
val b2 = newInst(5, "B")
b1.compare(b2) // type mismatch error here
}
}
On the last line I get this error:
error: type mismatch;
found : b2.type (with underlying type A)
required: b1.Impl
Since the type of b2 is the same as the type of b1 (which is A), I expected this to not generate an error. For some reason these path-dependent types are different than the "underlying types" when using reflection. Why?
If I do not use reflection, it works:
val b1 = new B(4)
val b2 = new B(5)
b1.compare(b2) // no errors
(I do need to use reflection in my case). Can newInst()
return the object as class "B", using reflection? Would this help? Is there type erasure when using abstract types?
This is the only reference I found (on this forum) about the same error, but it may not be related.
This doesn't have anything to do with reflection. The type of
b1
andb2
isA
(since that's the return type ofnewInst
). For callb1.compare(b2)
to compile,b2
must have typeb1.Impl
. The compiler knows only that it is some subtype ofA
, but it doesn't know which one. Since you can't pass anA
where some subtype ofA
is required, you get an error.In the example
both variables have type
B
, andB#Impl
isB
, so everything typechecks.