A Complete Guide to Variance in Java and Scala

Sinisa Louc
Nov 22, 2015 · 17 min read

First, a little background

Bounds to the rescue

public void process(List<? extends Car> list) { ... }
public void process(List<? super Car> list) { ... }
List<? extends Integer> a = new ArrayList<Integer>();
List<? super Integer> b = new ArrayList<Integer>();
a.add(3); // fails; let’s try with null
a.add(null); // works
b.add(3); // no problem here
Integer ai = a.get(0); // no problem here either
Integer bi = b.get(0); // fails; let’s try with Object
Object o = b.get(0); // works
                upper bound            lower bound
null ------------------- Car ------------------- Object

What about Scala?

public class Foo<T> { ... }
...
Foo<? extends Integer> covariantFoo = new Foo<Integer>();
Foo<? super Integer> contravariantFoo = new Foo<Integer>();
class CovariantFoo[+T] { ... }
class ContravariantFoo[-T] { ... }
...
val covariantFoo = new CovariantFoo[Int]()
val contravariantFoo = new ContravariantFoo[Int]()
Covariance                           
if A is a subtype of B then:
Java: L<A> is a subtype of L<? extends B> (use-site)
Scala: L[A] is a subtype of L[_ <: B] (use-site)
L[A] is a subtype of L[+B] (declaration-site)
Contravariance
if A is a supertype of B then:
Java: L<A> is a subtype of L<? super B> (use-site)
Scala: L[A] is a subtype of L[_ >: B] (use-site)
L[A] is a subtype of L[-B] (declaration-site)

Variance in function subtyping

trait Function1[-S, +T] { def apply(x: S): T }
def getCarInfo: Car => AnyRef
1   /**
2 * Remember! In Scala, every function that takes one argument
3 * is an instance of Function1 with signature:
4 *
5 * trait Function1[-T, +S] extends AnyRef
6 */
7
8 class Vehicle(val owner: String)
9 class Car(owner: String) extends Vehicle(owner)
10
11 object Printer {
12
13 val cars = List(new Car("john"), new Car("paul"))
14
15 def printCarInfo(getCarInfo: Car => AnyRef) {
16 for (car <- cars) println(getCarInfo(car))
17 }
18 }
19
20 object Customer extends App {
21
22 val getOwnerInfo: (Vehicle => String) = _.owner
23
24 Printer.printCarInfo(getOwnerInfo)
25 }
def printCarInfo(getCarInfo: Car => AnyRef)
val getOwnerInfo: (Vehicle => String) = _.owner

References


Javarevisited

An humble place to learn Java and Programming better.

Sinisa Louc

Written by

Backend Engineer at Spotcap (https://www.spotcap.com), Berlin. I blog about Scala and functional programming in general. Find me on Twitter (link below).

Javarevisited

An humble place to learn Java and Programming better.