Skip to content

Instantly share code, notes, and snippets.

@matklad
Last active October 31, 2016 09:54
Show Gist options
  • Save matklad/ede24faeb2a1bed85d8251fb9e9a169f to your computer and use it in GitHub Desktop.
Save matklad/ede24faeb2a1bed85d8251fb9e9a169f to your computer and use it in GitHub Desktop.
package org.rust;
import java.util.ArrayList;
import java.util.List;
public class Main {
private static class Base {
}
private static class Derived extends Base {
void foo() {
}
}
public static void main(String[] args) {
// covariantArray();
covariantGenericArray();
}
static void covariantArray() {
// Arrays in Java are covariant, which is type unsound.
// So, assignments to array elements are checked at
// runtime to prevent type errors.
Derived[] xs = {new Derived()};
unsafe1(xs);
}
static void unsafe1(Base[] xs) {
// This assignment is type unsound, but it's checked at runtime,
// so this is moderately acceptable.
xs[0] = new Base(); // ArrayStoreException!
}
static void covariantGenericArray() {
// Generics use type erasure. This is nice by itself,
// (no C++ code bloat), but interacts badly with
// covariant arrays, because run time checks do not work.
// Arrays are to blame, of course, but still...
List<Derived>[] bad = new List[1];
unsafe2(bad);
List<Derived> listOfBaseActually = bad[0];
System.out.println(listOfBaseActually.get(0));
// Kaboom! There is no `foo`!
listOfBaseActually.get(0).foo();
}
static void unsafe2(List<? extends Base>[] xs) {
ArrayList<Base> bases = new ArrayList<Base>();
bases.add(new Base());
// This assignment violates type safety.
// However it is not checked at runtime, because at run time
// List<Base> and List<Derived> have exactly the same type.
xs[0] = bases; // No exception here :(
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment