Have a look at this (arguably stupid) code:
public <T extends Appendable & Closeable> void doStuff(T object)
throws IOException{
object.append("hey there");
object.close();
}
I know that the compiler removes generic information, so I'm interested in Java 1.4 code equivalent to what the compiler does (I'm pretty sure the compiler doesn't rearrange the source code, so I am asking for an equivalent Java source version which naive people like myself can understand)
Is is something like this:
public void doStuff(Object object)
throws IOException{
((Appendable)object).append("hey there");
((Closeable)object).close();
}
Or rather like this:
public void doStuff(Object object)
throws IOException{
Appendable appendable = (Appendable) object;
Closeable closeable = (Closeable) object;
appendable.append("hey there");
closeable.close();
}
Or even like this:
public void doStuff(Appendable appendable)
throws IOException{
Closeable closeable = (Closeable) appendable;
appendable.append("hey there");
closeable.close();
}
Or yet another version?
Whenever you want to restrict the type parameter to subtypes of a particular class you can use the bounded type parameter. If you just specify a type (class) as bounded parameter, only sub types of that particular class are accepted by the current generic class. These are known as bounded-types in generics in Java.
A type parameter can have multiple bounds.
In a nutshell, generics enable types (classes and interfaces) to be parameters when defining classes, interfaces and methods. Much like the more familiar formal parameters used in method declarations, type parameters provide a way for you to re-use the same code with different inputs.
Signature of the method looks like public void doStuff(Appendable appendable)
, because
The order of types in a bound is only significant in that the erasure of a type variable is determined by the first type in its bound, and that a class type or type variable may only appear in the first position.
(JLS §4.4 Type Variables)
This behaviour may be important if you use reflection to access this method.
Another use of this behaviour is retaining binary compatibility with pre-generic interfaces, as described in Generics Tutorial, section 10 (thanks to Mark Peters for pointing it out). That is,
public static <T extends Object & Comparable<? super T>> T max(Collection<T> coll)
is binary compatible with its pre-generic version returning Object
.
Method body is an equivalent of the following, but I think it's implementation details:
appendable.append("hey there");
((Closeable) appendable).close();
I couldn't wait, I had to go and answer my own question. The answer is a combination of my first and third versions: the first bound is used as variable type, and the object is casted to the second bound whenever needed. This is the resulting byte code (I added a single line break for readability):
// Method descriptor #20 (Ljava/lang/Appendable;)V
// Signature: <T::Ljava/lang/Appendable;:Ljava/io/Closeable;>(TT;)V
// Stack: 2, Locals: 2
public void doStuff(java.lang.Appendable object) throws java.io.IOException;
0 aload_1 [object]
1 ldc <String "hey there"> [26]
3 invokeinterface java.lang.Appendable.append(java.lang.CharSequence) :
java.lang.Appendable [28] [nargs: 2]
8 pop
9 aload_1 [object]
10 checkcast java.io.Closeable [34]
13 invokeinterface java.io.Closeable.close() : void [36] [nargs: 1]
18 return
Line numbers:
[pc: 0, line: 14]
[pc: 9, line: 15]
[pc: 18, line: 17]
Local variable table:
[pc: 0, pc: 19] local: this index: 0 type: rumba.dumba.Bumba
[pc: 0, pc: 19] local: object index: 1 type: java.lang.Appendable
Local variable type table:
[pc: 0, pc: 19] local: object index: 1 type: T
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With