Reputation: 1997
Suppose we define an interface as:
trait Foo[A] {
val value: A
def perform(v: A): Unit
}
and compile this code:
val n: Foo[_] = null
n.perform(n.value)
It looks perfect... but we get a mysterious error:
error: type mismatch;
found : n.value.type (with underlying type _$1)
required: _$1
n.perform(n.value)
^
So... why does this happen? I know how to get around this; I am just curious. Thank you!
Upvotes: 4
Views: 117
Reputation: 31724
Lets first look at Foo[X] forSome {type X}
. It means: For type of all things for which there is some T such that they are of type Foo[T]
Note T
is not explicitly mapped to some type. So it is some unknown type.
Compiling with -Xprint:all
gives some more info
jatinpuri@jatin:~/Desktop$ scalac -Xprint:all T.scala
[[syntax trees at end of parser]] // T.scala
package <empty> {
...
abstract trait Foo[A] extends scala.AnyRef {
val value: A;
def perform(v: A): Unit
};
val n: Foo[_$1] forSome {
<synthetic> type _$1
} = null;
n.perform(n.value)
}
}
So n.value
returns type _$1
. But n.perform() also expects a wild type. And two unknown types cant be same. To explain further:
scala> trait Foo[A] {
| val value: A
| def perform(v: A): Unit
| type i = A
| }
defined trait Foo
scala> type I = Foo[X] forSome {type X}
defined type alias I
scala> val n : I = null;
n: I = null
scala> n.value:(I#i)
<console>:16: error: type mismatch;
found : n.value.type (with underlying type X)
required: X
n.value:(I#i)
I#i is X
. But n.value returns something else
Upvotes: 3
Reputation: 44007
You are not using a raw type here, you are using a wildcard type. This is similar to Java's
List<?> list = null;
list.add(list.get(0));
It does not compile as the compiler works in several steps:
n.value
is returned, the compiler marks the value to represent a wildcard.n.perform
is executed where the compiler only knows that the argument is another wildcard.Two wildcards are however not necessarily identical. Instead, there are two wildcards issued. In order to make this call, you would need to apply the so-called get-and-put principle. The get an put principle basically implies that you temporarly name the wildcard type such that the Scala compiler can infer that both wildcards are identical. Doing so, the following code compiles:
val n: Foo[_] = null
def rebox[A](x: Foo[A]) = x.perform(x.value)
rebox(n)
and throws a NullPointerException
.
Upvotes: 3