20

I know this question has been asked before, but either the answers don't apply to this case, or I don't understand them.

Basically, why doesn't the following (simple example that recreates my problem) work ?

class Test[+T] {
    var list: List[T] = _
}

The problem I am having is that I have an object, where I want to pass in an instance of Test[Nothing] (the empty Test), and this doesn't work unless I make Test co-variant in T.

Andrii Abramov
  • 10,019
  • 9
  • 74
  • 96
sksamuel
  • 16,154
  • 8
  • 60
  • 108

1 Answers1

27

Making test covariant in T means that Test[A] is a subtype of Test[Any] for any A. So lets create a Test:

val test_string = new Test[String]

Now we have a Test[String] and the contained list is type List[String].

Since Test[String] is a subtype of Test[Any], the following should be allowed:

val test_any : Test[Any] = test_string

And now, we have a Test[Any], and therefore test_any.list is type List[Any], which means the following should be valid:

test_any.list = List[Any]()

Which means we just assigned a List[Any] to test_strings list member, which shouldn't be allowed, since that is supposed to be a List[String], not a List[Any]. It also means you could prepend anything at all to the list, since it is type List[Any].

stew
  • 11,276
  • 36
  • 49
  • 1
    So how can I have my containing list vary with the type parameter of the Test class, or is that not possible? – sksamuel Jan 29 '13 at 15:05
  • 1
    @monkjack You could make `list` a `val`: `class Test[+T](val list: List[T])`, but this might not always be option, depending on when you initialise the list. – Malte Schwerhoff Jan 29 '13 at 15:14
  • Yeah I noticed that works, unfortunately I'm actually json'ing this and Jackson won't unmarshall to a class constructor AFAIK. I might see if I can get away with a case class in my uses. – sksamuel Jan 29 '13 at 15:16
  • 11
    The specific issue here is that defining `list` as a `var` the compiler automatically provides the mutator method `def list_=(l: List[T]): Unit`, and in this method the `T` type parameter is not in *covariant* position. The same would happen if you try to set `T` as *contravariant*, because of the corresponding accessor method. There's no workaround other than setting `T` as *nonvariant* or using a `val` for your list. – pagoda_5b Jan 29 '13 at 16:12