Instructor: James Riely
class A { int x; }
class B extends A { float y; }
class C extends A { char c; }
void f (B b) {
A a = b; // upcast always safe
}
void g (A a) {
B b = (B) a; // downcast must be checked
}
f (new B()); // OK
g (new C()); // ClassCastException
A x = new A ();
B y = new B ();
x = y; // B ok when A expected
void aConsumer (A x) { ... }
aConsumer (y); // B ok when A expected
B bProducer () { ... }
x = bProducer (); // B ok when A expected
B
when an A
is expected
B
is a subtype of A
-- written B<:A
y:B
and B<:A
then y:A
(upcast)
<:
is a partial order on types
X<:X
Duck<:Bird
Bird<:Animal
Duck<:Animal
Top
type
X<:Top
(greater than all other types)
java.lang.Object
above reference types
scala.Any
above all types
scala.AnyRef
above reference types
import java.io.FileInputStream
val xs:List[AnyRef] = List ("hello", FileInputStream ("a.txt"))
val ys:List[Any] = List ("hello", 1)
Bottom
type
Bottom<:X
(less than all other types)
Bottom
is scala.Nothing
Nil
Nil:List[Nothing]
List[Nothing]<:List[X]
val mynil1:List[Int] = Nil
val xs1:List[Any] = "hello"::mynil1 // Best type possible
val mynil2:List[Nothing] = Nil
val xs2:List[String] = "hello"::mynil2 // Best type possible
class A { def f () = 1 }
class B extends A:
override def f () = 2
def g () = 3
var as:List[A] = List (A(), B()) // OK, because B <: A
var bs:List[B] = List (B(), B()) // OK
as = bs
as(1).f()
// mutated as --- OK, because List[B] <: List[A]
val res4: Int = 2
List
is covariant
B<:A
then
List[B]<:List[A]
T[-]
is covariant if
B<:A
implies
T[B]<:T[A]
class A { def f () = 1 }
class B extends A:
override def f () = 2
def g () = 3
var as:Array[A] = Array (A(), B()) // OK
var bs:Array[B] = Array (B(), B()) // OK
as = bs
^
error: type mismatch;
found : Array[B]
required: Array[A]
Note: B <: A, but class Array is invariant in type T.
You may wish to investigate a wildcard type such as `_ <: A`
class A { def f () = 1 }
class B extends A:
override def f () = 2
def g () = 3
var as:Array[A] = Array (A(), B()) // OK
var bs:Array[B] = Array (B(), B()) // OK
as = bs // ERROR, because Array[B] NOT <: Array[A]
as(0) = A() // OK, because as:Array[A]
bs(0).g() // Unsafe access
class A { def f () = 1 }
class B extends A:
override def f () = 2
def g () = 3
var as:Array[_ <: A] = Array (A(), B()) // OK
var bs:Array[B] = Array (B(), B()) // OK
as = bs // OK, because Array[B] <: Array[_ <: A]
as(0) = A()
^
error: type mismatch;
found : A
required: _$1 where type _$1 <: A
Array[_ <: A]
class A { def f () = 1 }
class B extends A:
override def f () = 2
def g () = 3
var as:Array[A] = Array (A(), B()) // OK
var bs:Array[_ >: B] = Array (B(), B()) // OK
bs = as // OK, because Array[A] <: Array[_ >: B]
bs(0) = B() // OK
bs(0).g() // Unsafe access
^
error: value g is not a member of _$1
|
class A { def f () = 1 }
class B extends A:
override def f () = 2
def g () = 3
var as:Array[A] = Array (A(), B()) // OK
var bs:Array[_ >: B] = Array (B(), B()) // OK
bs = as // OK, because Array[A] <: Array[_ >: B]
bs(0) = B() // OK
bs(0) // OK
val res1: Any = B@d271a54
|
|
public class Driver {
public static void main (String[] args) {
B[] bs = new B[] { new B (), new B () };
A[] as = bs; // OK, because covariant
as[0] = new A (); // ArrayStoreException
bs[0].g();
}
}
$ javac Driver.java
$ java Driver
Exception in thread "main" java.lang.ArrayStoreException: A
at Driver.main(Driver.java:5)
static void sort(Object[] xs) { ... }
String[] ss = ...;
sort(ss); // requires covariance
static <X extends Comparable<? super X>> void sort(X[] xs) { ... }
def sort[X <: Comparable[_ >: X]] (Array[X] xs) = ...
class A implements Comparable<A>{}
class B extends A {}
B[] bs = ...;
sort(bs); // B's are comparable as A's
class A {} // Animal
class B extends A {} // Bird
class D extends B {} // Duck
trait Source[+X] { def get () : X } // Covariant
trait Sink [-X] { def put (x:X) : Unit } // Contravariant
class Ref [ X] (var contents:X) // Invariant
extends Source[X] with Sink[X] {
def get () = contents
def put (x:X) = contents = x
}
val ref : Ref [B] = Ref[B] (B())
val src : Source[A] = ref
val snk : Sink [D] = ref
val d = D()
snk.put(d)
val r = ref.get()
val s = src.get()
val d: D = D@595713f3
val r: B = D@595713f3
val s: A = D@595713f3
trait Producer[+Y] { def apply () : Y } // Covariant
trait Consumer[-X] { def apply (x:X) : Unit } // Contravariant
trait Function[-X,+Y] { def apply (x:X) : Y } // Both
trait Operator[X] { def apply (x:X) : X } // Invariant
trait Producer[-Y] { def apply () : Y }
^
error: contravariant type Y occurs in covariant position
trait Consumer[+X] { def apply (x:X) : Unit }
^
error: covariant type X occurs in contravariant position
Covariant | Contravariant | Invariant | |
---|---|---|---|
Relationship | T[B]<:T[A] |
T[A]<:T[B] |
|
Collection | List[X] |
Array[X] |
|
Read only | Write only | Read / Write | |
Reference | Source[X] |
Sink[X] |
Ref[X] |
Producer | Consumer | Operator | |
Function | Unit=>X |
X=>Unit |
X=>X |
Here's another example