Duplicate ByteBuffer.array() method in Java 6
I am using Java 1.6.0. When running the following code:
import java.lang.reflect.Method;
import java.nio.ByteBuffer;
publicclass test
{
publicstaticvoid main (String[] args)
{
for (Method method : ByteBuffer.class.getMethods()){
System.out.println(method);
}
}
}
I get, amongst the output, two "array" methods, both taking 0 arguments:
publicfinalbyte[] java.nio.ByteBuffer.array()
public java.lang.Object java.nio.ByteBuffer.array()
How is that possible? I thought that is not allowed under the Java language.
When I run the same code on Java 5 (1.5.0_08), it only returns one such method:
publicfinalbyte[] java.nio.ByteBuffer.array()
Please explain.
# 1
I can't explain the differences between the Java versions, but as to why you have
two "array" methods in Java 6, here's what I found out:
First of all, in the source code for ByteBuffer, there is only the "byte[] array()"
method. In the class file, of course, there is also a method "Object array()". It is
tagged as "bridge synthetic", which means 1) it was created automatically by
the Java compiler, and 2) that it is a bridge method. I believe that bridge methods were
introduced in connection with generics, but I could also be totally wrong on that one
(I never used generics, actually) But maybe as a start, see:
http://java.sun.com/docs/books/jls/third_edition/html/expressions.html#15.12.4.5
There, they elaborate on the process of method invocation, and they mention bridge
methods under "Discussion" (search for "bridge"). It looks rather complicated, though, I have to admit...
> I get, amongst the output, two "array" methods, both taking 0 arguments:
>
> public final byte[] java.nio.ByteBuffer.array()
> public java.lang.Object java.nio.ByteBuffer.array()
>
> How is that possible? I thought that is not allowed under the Java language.
Yeah, that's what I thought, too. But it is, obviously. It would be intersting
to know whether this has always been possible (and for what purpose, then), or was
introduced with generics (presumably?), to allow for those bridge methods, maybe...
# 2
It's not allowed under the Java language, but that doesn't stop the compiler from synthesizing it in the output, which is in bytecode, not the Java language.
ejpa at 2007-7-29 14:47:30 >

# 3
Yes, you are right, and what I wrote there is nonsense. I should have spent some more time
to think before posting this. I was referring to the bytecode language actually, because for
some strange reason, I believed that two or more methods differing just in return type would
be causing problems here, too. I realized now that this is not the case at all, as the various
INVOKE instructions use the method's descriptor, which includes the return type and is thus
unambiguous. So I guess such methods were always permitted in bytecode...
(they cannot be declared in Java programming language source code, as pointed out by ejp!
-- although they can be reflected, as was demonstrated by OP)
# 4
So when I call someByteBuffer.array() in my Java source code, how does the Java compiler know which method I want to invoke?
# 5
So when I call someByteBuffer.array() in my Java source code, how does the Java compiler know which method I want to invoke?
# 6
The one that matches the signature embedded at the call site.
ejpa at 2007-7-29 14:47:30 >

# 7
I mean when I write a call to it in my source code, when the compiler compiles it into bytecode, which signature does it put in?
# 8
Well obviously it puts in the correct one, that returns byte[], the one you wanted. Otherwise java would be severely broken, and it isn't.
I expect the compiler ignores the signatures(s) marked as 'bridge synthetic'.
ejpa at 2007-7-29 14:47:30 >

# 9
koksalliman wrote:
> I mean when I write a call to it in my source code, when the compiler compiles it into bytecode, which signature does it put in?
Well, where would you probably start to look for such information, if you were really interested to know? I provided the following link
in my first reply:
http://java.sun.com/docs/books/jls/third_edition/html/expressions.html#15.12.4.5
If you bothered to read what is written there, you may get here:
http://java.sun.com/docs/books/jls/third_edition/html/expressions.html#15.12.3
and eventually here (Choosing the Most Specific Method), where you may find what you are looking for (I hope):
http://java.sun.com/docs/books/jls/third_edition/html/expressions.html#15.12.2.5
It says:
"If more than one member method is both accessible and applicable to a method invocation, it is necessary to choose one
to provide the descriptor for the run-time method dispatch. The Java programming language uses the rule that the most specific method is chosen.
The informal intuition is that one method is more specific than another if any invocation handled by the first method could be passed on to the other one without a compile-time type error."
Now a bit further down (out of context here, but still comprehensible):
"[...] then the most specific method is chosen arbitrarily among the subset of the maximally specific methods that have the most specific return type. [...]"
So I'd say "byte[]" is more specific than "Object", in this case, and the subset contains only one method. But given the complexity of this stuff, I'm glad it's automated. It'll still be interesting to investigate this some time, e.g. an instance of which class is the "Object array()" version supposed to return, as opposed to the "byte[] array()" version(?)...
# 10
And it seems that the conclusion
> [...] they cannot be declared in Java programming language source code [...]
is only part of the truth here. You can actually declare such methods with different return type in Java language source code -- just not
in the same class. When looking at ByteBuffer, the original declaration of array() method is in superclass Buffer, but there it is declared as
"Object array()". So I threw together the following, which does (to my surprise) compile with -source 1.5 (not with 1.4 and below,
but I didn't expect it to, anyway):
// Bridge.java
public class Bridge {
public static void main(final String[] args) {
final C c = new C();
Object obj;
obj = ((A) c).array(); // invokevirtual A.array:()Ljava/lang/Object;
obj = c.array();// invokevirtual C.array:()[B
obj = ((B) c).array(); // invokevirtual B.array:()[B
// ...
}
}
//-
abstract class A {
public Object array() { return null; }
}
//-
class B extends A {
public byte[] array() { return null; }
// [bridge synthetic] Object array() { return invokevirtual B.array:()[B }
}
class C extends B {
Object test() {
Object obj;
obj = ((A) this).array(); // invokevirtual A.array:()Ljava/lang/Object;
obj = ((B) this).array(); // invokevirtual B.array:()[B
obj = array(); // invokevirtual C.array:()[B
return null;
}
}
//-
class B2 extends A {
public Object array() { return null; } // overrides
}
class C2 extends B2 {
public byte[] array() { return null; }
// [bridge synthetic] Object array() { return invokevirtual C2.array:()[B }
}
What I still don't get, though, is how this is connected to generics, and for what this bridge method is required when it just
forwards the call -- it has no parameters like in the JLS "Discussion"? Maybe this is just a special case of the things
discussed there, and the bridge method is simply created to "obey the language rules"? What is the benefit, wrt a parameter-less method?
Then again, I guess I also don't really wanna know, actually... this generics stuff messes my head up.
