I ran the example and I got the same result. The result that is provided, and the one I got, makes sense. Can someone explain to me what is "broken" about this?

Broken down you:

Create 3 classes
- Unsound: Unsound contains a static generic method. Two types are requested (T and U). T is the parameter and the U is the return type. From now on T will be called CoerceInputType and U will be called CoerceOutputType
- Constrain: A class that has two generic types it requests. A being the first type, and B who is the second type. A restriction is placed on B saying that B extends A. We will call A ConstraintType and B ExtendedConstraintType
- Bind: A class that takes one generic parameter of type A. Here we will call A BindType

What is happening here?

Your execution stack:

- *Call* Unsound.coerce. Tell it CoerceInputType is an Integer and CoerceOutputType is a String. You pass into coerce a value of '0' with the type CoerceInputType (an Integer class) and accept a result with the return type CoerceOutputType (a String class)
- *Assign* In Unsound.coerce you define a type Constraint<CoerceOutputType, ANYTHING extends CoerceInputType>. This means that these type annotations will be propagated through anything this is passed into.
- *Assign* you create an instance of bind with the type CoerceOutputType.

What this currently looks like

<Output, AnyType that extends Input> Output ourFunction(Input i) {
return i
}

What happens: You get an exception as Integer is not castable to a String. You can look further down to the only link in this page to see where that cast is coming from in the bytecode.

The authors seem to be surprised that this will not execute? Or maybe that this doesnt? This is completely expected behavior. Maybe not to someone who hasen't worked in Java and is coming from another VM but for me this is not surprising. Why? Here's a list:

The ways you can trigger a type change:

1. Auto boxing
2. Casting

For integers and strings, these two type conversions will not work. Here is a much more direct explanation of what's happening here:

class Test {
public static void main(String[] args) {
String a = "Hello World";
Integer b = 10;
a = b;
}
}

Running this you will get the following error:

test.java:5: error: incompatible types: Integer cannot be converted to String
a = b;
^
1 error

This is because Integer cannot auto (un)box to String. This exception happens at compile time. I'll get into why in a bit.

You might also ask, "But gravypod, what about an explicit cast, will that work?" No, it will not!

test.java:5: error: incompatible types: Integer cannot be converted to String
a =(String) b;
^
1 error

This will not work at compile time as well.

Now for the next section: why will this not be picked up at compile time? Well that's a hard one to answer, the basic answer is the method isn't existent yet. Or to be closer to the truth, the method's types don't exist yet. When you use generics you're creating a method that will work on a base set of types. There isn't necessarily hard type checking on generics though. These are mainly there for the programmer, not the runtime. The runtime will operate on any data and is just expecting casting to change between types.

When run though a decompiler this is the output of Unsound.class: http://hastebin.com/sitamomogu.java

All that has happened is the compiler couldn't follow the type annotations at compile time and it was left for runtime. This should be improved, but I don't see what is broken. The time of the error just shifted. This is the same thing that happens with null. The error is not compile time when you are going to have an NPE. Instead they are runtime.

Here is example code that compiles perfectly fine:

public class Test {
public static void main(String[] args) {
String a = null;
System.out.println(a.getClass().getSimpleName());
}
}

When run you will get this exception:

Exception in thread "main" java.lang.NullPointerException
at Test.main(Test.java:7)

I don't think that this is broken behavior as this is consistent with the way the JVM is meant to operate. Types are suggestions to the VM. This to me is a side effect of auto (un)boxing.

Maybe I'm wrong. I hope someone could tell me why if so.

Edit: Please read https://news.ycombinator.com/item?id=13051036 as this explanation is much more elegant and gets the idea across in a simpler way.