T doesn't mean Integer, it must be valid for Integer or any class that extends from it. Let's say that StrangeInteger extends from Integer and replace T with StrangeInteger:
void passVal (StrangeInteger t) {
Integer number = 5;
t = (StrangeInteger) number;
}
It attempts to assign a Integer variable to a StrangeInteger variable, you can't do that unless number was a StrangeInteger or a derived class in the first place. In fact your code should (conceptually) throw an Exception at runtime unless t is an Integer, but it won't actually do that in this case due to type erasure (see Edit 2).
The situation is similar to:
Object obj = "Hello"
String t = (String)obj; // this will not fail, but requires a cast
Object obj2 = getDBConnection();
String t2 = (String)obj2; // this will fail at runtime
Edit: Integer is indeed final, so T can only be Integer, but it's possible that the compiler is not checking if the upper bound is final, after all it makes little sense for an upper bound to be final, so allowing that special case adds complexity for very little real gain.
Edit 2:
TL; DR: You are confusing upper and lower bounds, but there are caveats with type erasure. This will break as soon as you do anything that is worth doing using generics rather than just using the base type.
English is not my first language so I may be not totally clear.
I think you are struggling with the difference between using a generic type with an upper bound and just using the upper bound as type. The idea of generics (from C++ and other languages) is that the code must be valid if you replace T by any type T allowed by the bounds, so you can't call any method that is not defined in the upper bound.
A being an upper bound on T also means that you can always assign a T object to an A variable. You can't assign safely an A object to a T variable (unless A == T), you could only do that if A was a lower bound on T, not an upper bound. Also see Understanding upper and lower bounds on ? in Java Generics.
Java uses type erasure to implement generics, there are a few advantages, but that causes some limitations that are not always apparent. Because of type erasure in this case the cast itself won't fail, after type checking T is replaced by the upper bound in the type erasure step, i.e. (T)number is replaced by (Integer)number. An exception will still occur if you do anything that causes a cast to the subclass, for example if you return the altered t and assign the result to a variable of the subclass, because the compiler adds an implicit cast.
This will also fail if you call a method that depends on an subclass of T, which is a common pattern, for example:
List<Person> persons = ...
Comparator<Person> nameComparator = (p1,p2) -> p1.getName().compareTo(p2.getName())
java.util.Collections.sort(persons,nameComparator);
The following code sample display the behavior in several cases. I used System.err on everything to avoid order issues in the output.
import java.util.function.Consumer;
import java.util.function.Function;
class A {
@Override public String toString(){ return "A";}
public String foo(){ return "foo";}
}
class B extends A {
@Override public String toString(){ return "B";}
public String bar(){ return "bar";}
}
class C extends B { }
public class Main {
public static void main(String[] args) {
Function<A,String> funA = a -> a.foo();
Function<B,String> funB = b -> b.bar();
Function<C,String> funC = c -> c.bar();
Consumer<B> ignoreArgument = b -> {
System.err.println(" Consumer called");
};
B b = new B();
System.err.println("* voidTest *");
voidTest(b);
System.err.println("------------");
System.err.println("* returnTest *");
returnTest(b);
System.err.println("returnTest without using result did not throw");
System.err.println("------------");
try {
System.err.println("Returned " + returnTest(b).toString());
System.err.println("returnTest: invoking method on result did not throw");
}
catch(Exception ex) {
System.err.println("returnTest: invoking method on result threw");
ex.printStackTrace();
}
System.err.println("------------");
B b2 = null;
try {
b2 = returnTest(b);
System.err.println("returnTest: assigning result to a B variable did not throw");
}
catch(Exception ex) {
System.err.println("returnTest: assigning result to a B variable threw");
ex.printStackTrace();
}
System.err.println("------------");
System.err.println("* functionTest funA *");
functionTest(b, funA);
System.err.println("------------");
System.err.println("* functionTest funB * ");
functionTest(b, funB);
System.err.println("------------");
System.err.println("* consumerTest *");
consumerTest(b, ignoreArgument);
// The following won't work because C is not B or a superclass of B
// Compiler error functionTest(T, Function<? super T,String>) is not applicable for the arguments (B, Function<C,String>)
// functionTest(b, funC);
}
private static <T extends A> void voidTest(T t){
System.err.println(" Before: " + t.toString());
t = (T)new A(); // warning Type safety: Unchecked cast from A to T
System.err.println(" After: " + t.toString());
}
private static <T extends A> T returnTest(T t){
System.err.println(" Before: " + t.toString());
t = (T)new A();
System.err.println(" After: " + t.toString());
return t;
}
private static <T extends A> void functionTest(T t, Function<? super T,String> fun) {
System.err.println(" fun Before: " + fun.apply(t));
t = (T)new A();
try {
System.err.println(" fun After: " + fun.apply(t));
}
catch(Exception ex) {
System.err.println(" fun After: threw");
ex.printStackTrace();
}
}
private static <T extends A> void consumerTest(T t, Consumer<? super T> c) {
System.err.print(" Before: ");
c.accept(t);
t = (T)new A();
try {
System.err.println(" After: ");
c.accept(t);
System.err.println(" c.accept(t) After: worked");
}
catch(Exception ex) {
System.err.println(" c.accept(t) After: threw");
ex.printStackTrace();
}
}
}
The output under OpenJDK 11 is:
* voidTest *
Before: B
After: A
------------
* returnTest *
Before: B
After: A
returnTest without using result did not throw
------------
Before: B
After: A
returnTest: invoking method on result threw
java.lang.ClassCastException: class A cannot be cast to class B (A and B are in unnamed module of loader 'app')
at Main.main(Main.java:35)
------------
Before: B
After: A
returnTest: assigning result to a B variable threw
java.lang.ClassCastException: class A cannot be cast to class B (A and B are in unnamed module of loader 'app')
at Main.main(Main.java:45)
------------
* functionTest funA *
fun Before: foo
fun After: foo
------------
* functionTest funB *
fun Before: bar
fun After: threw
java.lang.ClassCastException: class A cannot be cast to class B (A and B are in unnamed module of loader 'app')
at Main.functionTest(Main.java:83)
at Main.main(Main.java:57)
------------
* consumerTest *
Before: Consumer called
After:
c.accept(t) After: threw
java.lang.ClassCastException: class A cannot be cast to class B (A and B are in unnamed module of loader 'app')
at Main.consumerTest(Main.java:97)
at Main.main(Main.java:60)
I'm not entirely sure resultTest why didn't caused an exception if the result is completely ignored, maybe a cast is not required by the language in that case, or the compiler removed it. Calling a method defined in the upper bound on the result still caused an exception. Finally an observation from consumerTest is that it didn't need to call bar() to cause a ClassCastException, it just needed to pass t to the consumer that expects a B argument.