Bounded generic type parameters like A extends Something are not writable (as well upper-bounded wild cards ? extends Something). Any attempt to assign anything to the variable a apart from other variable of type A and null will fail.
In order to understand why, let's consider the following code:
public class Parent {}
public class Child extends Parent {}
public class GrandChild extends Child {}
public class Holder<A extends Parent> {
public A a;
public void setA(A a) {
this.a = a;
}
}
A in the Holder class is just a place-holder for the type that will be provided at runtime. It's a way to tell the compiler that we don't know for now what the type is. But it will be a particular type that has a Parent class in its inheritance chain. The compiler will take that information into account while checking whether the operations done on the variable a are safe.
In the code below, the compiler will disallow to assign an object of Child as a value for a, because it's incompatible with a type GrandChild (that happens to be the actual type for A). Only instances of GrandChild and its subtype can be assigned without issues.
public static void main(String[] args) {
Holder<GrandChild> grandChildHolder = new Holder<>();
grandChildHolder.setA(new GrandChild()); // no issues
grandChildHolder.setA(new Child()); // compilation error
}
Similarly, the following assignments inside the Holder class will not succeed because type A will be known only Holder class will get instantiated. And compile doesn't possess information whether it'll be Child, GrandChild, etc., therefore it will not consider these operations to be safe.
public class Holder<A extends Parent> {
public A a;
// instance initialither block (runs when Holder object is being created)
{
a = new Child(); // compilation error - type A could be a GrandChild
a = new GrandChild(); // compilation error - type A could potentially be represented by class incompatible with GrandChild
a = null; // no issues because null is a valid value for any type
}
public void setA(A a) {
this.a = a;
}
}
Upper-bounded generic parameters like A extends Parent are useful when want to make a class or method to be able to work with objects of different types and the same time impose a certain restriction on a range of the valid types in order to access the behavior of the Parent class (let's assume there are some like work(), goShoping(), etc.).
If you declare the holder without extends clause, just Holder<A> only methods of the Object class (hashCode(), equals(), toString()) will be accessible with variables and parameters of type A.
Also, clause extends Parent will allow the compiler to spot attempts to introduce an invalid type parameter:
Holder<String> stringHolder; // error:
// type argument String is not within the bounds of type A
AinA extends Parentcan be a different sub class likeOtherChild. Then the assignmentOtherChild a = new Child();will not work, they are different classes (and they don't extend from each other).A extends Parentmeans thatAis of an unknown type. The only constraint is that it must extendParent. But that could also be true for other classe, e.g.class Brother extends Parent {}.public A a;andpublic Parent a;are not the same thing. Like all generic type arguments,Ais always a specific, exact type. That type might be Parent itself, or that type might be Child, or that type might be something else entirely. So you cannot safely assume thatacan hold a Child instance, whenAmight be restricted to an entirely different type.