// Main class
public class TestCoCon {

 public static void main(String [] ar) {

  U u = new U();
  D d = new D();
  U w = new D();

  T t = new T();
  M m = new M();
  B b = new B();

  System.out.println("-- first test suite");
  u.cv(t);
  u.cv(m);
  u.cv(b);
  //u.ctv(t);
  System.out.println("ctv(B) in U cannot be applied to (T)");
  //u.ctv(m);
  System.out.println("ctv(B) in U cannot be applied to (M)");
  u.ctv(b);

  System.out.println("-- second test suite");
  d.cv(t);
  d.cv(m);
  d.cv(b);
  //d.ctv(t);
  System.out.println("cannot resolve symbol");
  d.ctv(m);
  d.ctv(b);  // NOW ACCEPTED!!!
  // previously ... the compiler error message was: "reference to ctv is ambiguous, both method ctv(B) in U and method ctv(M) in D match");

  System.out.println("-- third test suite");
  w.cv(t);
  w.cv(m);
  w.cv(b);
  //w.ctv(t);
  System.out.println("ctv(B) in U cannot be applied to (T)");
  //w.ctv(m);
  System.out.println("ctv(B) in U cannot be applied to (M)");
  w.ctv(b);
 }
}

// U class
public class U {
    public void cv(T t) {//covariance
        System.out.println("cv(T) in U ");
    }
    public void ctv(B t) {// contravariance
        System.out.println("ctv(B) in U ");
    }
}

// D class
public class D extends U {
    public void cv(M m) {//covariance
        System.out.println("cv(M) in D");
    }
    public void ctv(M m) {// contravariance
        System.out.println("ctv(M) in D");
    }
}

// T class
public class T { }

// M class
public class M extends T { }

// B class
public class B extends M { }

// run
-- first test suite
cv(T) in U
cv(T) in U
cv(T) in U
ctv(B) in U cannot be applied to (T)
ctv(B) in U cannot be applied to (M)
ctv(B) in U

-- second test suite
cv(T) in U
cv(M) in D
cv(M) in D
cannot resolve symbol
ctv(M) in D
ctv(B) in U

-- third test suite
cv(T) in U
cv(T) in U
cv(T) in U
ctv(B) in U cannot be applied to (T)
ctv(B) in U cannot be applied to (M)
ctv(B) in U