Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
714 views
in Technique[技术] by (71.8m points)

scala - covariant type A occurs in contravariant position in type A of value a

I have following class:

case class Box[+A](value: A) {

  def set(a: A): Box[A] = Box(a)

}

And the compiler complain:

Error:(4, 11) covariant type A occurs in contravariant position in type A of value a
  def set(a: A): Box[A] = Box(a)

I was searching a lot about the error, but could not find something useful that help me to understand the error.

Could someone please explain, why the error occurs?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

The error message is actually very clear once you understand it. Let's get there together.

You are declaring class Box as covariant in its type parameter A. This means that for any type X extending A (i.e. X <: A), Box[X] can be seen as a Box[A].

To give a clear example, let's consider the Animal type:

sealed abstract class Animal
case class Cat extends Animal
case class Dog extends Animal

If you define Dog <: Animal and Cat <: Animal, then both Box[Dog] and Box[Cat] can be seen as Box[Animal] and you can e.g. create a single collection containing both types and preserve the Box[Animal] type.

Although this property can be very handy in some cases, it also imposes constraints on the operations you can make available on Box. This is why the compiler doesn't allow you to define def set.

If you allow defining

def set(a:A): Unit

then the following code is valid:

val catBox = new Box[Cat]
val animalBox: Box[Animal] = catBox // valid because `Cat <: Animal`
val dog = new Dog
animalBox.set(dog) // This is non-sensical!

The last line is obviously a problem because catBox will now contain a Dog! The arguments of a method appear in what is called "contravariant position", which is the opposite of covariance. Indeed, if you define Box[-A], then Cat <: Animal implies Box[Cat] >: Box[Animal] (Box[Cat] is a supertype of Box[Animal]). For our example, this is of course non-sensical.

One solution to your problem is to make the Box class immutable (i.e. to not provide any way to change the content of a Box), and instead use the apply method defined in your case class companion to create new boxes. If you need to, you can also define set locally and not expose it anywhere outside Box by declaring it as private[this]. The compiler will allow this because the private[this] guarantees that the last line of our faulty example will not compile since the set method is completely invisible outside of a specific instance of Box.

If for some reason you do not want to create new instances using the apply method, you can also define set as follows.

def set[B >: A](b: B): Box[B] = Box(b)

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

1.4m articles

1.4m replys

5 comments

56.8k users

...