Type-erasure
As mentioned in Comments, Java Generics was implemented with type-erasure (see tutorial). This means the specific class information is not retained after compiling.
The opposite of type-erasure, where class info is retained, is known as reified generics (see this Question). Java does not have reified generics; Java has type-erasure.
Because of type-erasure in Java, your overloaded order
methods become ambiguous. You will get errors and warnings from your IDE and your compiler about the two order
methods clashing. After erasure, your two order
methods are indistinguishable.
public static void order( Box box ) { … }
public static void order( Box box ) { … }
We can resolve the clash by renaming these:
public static void order( Box<? extends Mammal> box ) { … }
public static void order( Box<? super Mammal> box ) { … }
… to distinct names:
public static void orderMammalOnDownToGorillaAndHuman( Box<? extends Mammal> box ) { … }
public static void orderMammalUpThroughObject( Box<? super Mammal> box ) { … }
Also because of type-erasure, your new T()
code makes no sense, as explained in the other Answer here, by rzwitserloot. The class info is not known, so new
cannot be invoked for an undefined class.
Example code
So let's rename those order
methods. And make some other tweaks so this code runs.
package work.basil.example.typing;
import java.util.Objects;
public class WildCards
{
public static void main ( String[] args )
{
WildCards.orderMammalOnDownToGorillaAndHuman ( new Box < Human > ( new Human ( ) ) );
WildCards.orderMammalOnDownToGorillaAndHuman ( new Box < Mammal > ( new Gorilla ( ) ) );
WildCards.orderMammalUpThroughObject ( new Box < Animal > ( new Gorilla ( ) ) );
// WildCards.orderMammalUpThroughObject ( new Box < Primate > (new Gorilla () ) ); // ERROR: incompatible types: work.basil.example.typing.Box<work.basil.example.typing.Primate> cannot be converted to work.basil.example.typing.Box<? super work.basil.example.typing.Mammal>
}
public static void orderMammalUpThroughObject ( Box < ? super Mammal > box )
{
System.out.println ( box.getClassNameOfMemberField ( ) );
}
public static void orderMammalOnDownToGorillaAndHuman ( Box < ? extends Mammal > box )
{
System.out.println ( box.getClassNameOfMemberField ( ) );
}
}
class Thing { }
class Creature extends Thing { }
class Animal extends Creature { }
class Mammal extends Animal { }
class Primate extends Mammal { }
class Gorilla extends Primate { }
class Human extends Primate { }
class Box < T extends Thing >
{
T object;
String getClassNameOfMemberField ( )
{
return Objects.isNull ( this.object ) ? "null" : this.object.getClass ( ).getName ( );
}
public Box ( T object )
{
this.object = object;
}
}
When run:
work.basil.example.typing.Human
work.basil.example.typing.Gorilla
work.basil.example.typing.Gorilla
Notice that our 4th call in main
fails to compile:
WildCards.orderMammalUpThroughObject ( new Box < Primate > (new Gorilla () ) );
We get an error:
incompatible types: work.basil.example.typing.Box<work.basil.example.typing.Primate> cannot be converted to work.basil.example.typing.Box<? super work.basil.example.typing.Mammal>
This makes sense. We said the orderMammalUpThroughObject
method takes an argument that is a Box
holding any object whose class is Mammal or higher. This means Mammal
, Animal
, Creature
, Thing
, and Object
. But the argument cannot be an object of the subclasses: Primate
, Gorilla
, Human
. So:
- Passing a
Box
of Animal
with a Gorilla
works.
- Passing a
Box
of Primate
with a Gorilla
fails, a violation of our declaration.
For more discussion of extends
versus super
, see this Answer, and Questions this and this.