Something called the "bridge method" concept related to Java Generics made me stop at a point and think over it.
Btw, I only know that it occurs at the
bytecode level and is not available
for us to use.
But I am eager to know the concept behind the "bridge method" used by the Java compiler.
What exactly happens behind the scenes and why it is used?
Any help with an example would be greatly appreciated.
It's a method that allows a class extending a generic class or implementing a generic interface (with a concrete type parameter) to still be used as a raw type.
Imagine this:
public class MyComparator implements Comparator<Integer> {
public int compare(Integer a, Integer b) {
//
}
}
This can't be used in its raw form, passing two Objects to compare, because the types are compiled in to the compare method (contrary to what would happen were it a generic type parameter T, where the type would be erased). So instead, behind the scenes, the compiler adds a "bridge method", which looks something like this (were it Java source):
public class MyComparator implements Comparator<Integer> {
public int compare(Integer a, Integer b) {
//
}
//THIS is a "bridge method"
public int compare(Object a, Object b) {
return compare((Integer)a, (Integer)b);
}
}
The compiler protects access to the bridge method, enforcing that explicit calls directly to it result in a compile time error. Now the class can be used in its raw form as well:
Object a = 5;
Object b = 6;
Comparator rawComp = new MyComparator();
int comp = rawComp.compare(a, b);
Why else is it needed?
In addition to adding support for explicit use of raw types (which is mainly for backwards compatability) bridge methods are also required to support type erasure. With type erasure, a method like this:
public <T> T max(List<T> list, Comparator<T> comp) {
T biggestSoFar = list.get(0);
for ( T t : list ) {
if (comp.compare(t, biggestSoFar) > 0) {
biggestSoFar = t;
}
}
return biggestSoFar;
}
is actually compiled into bytecode compatible with this:
public Object max(List list, Comparator comp) {
Object biggestSoFar = list.get(0);
for ( Object t : list ) {
if (comp.compare(t, biggestSoFar) > 0) { //IMPORTANT
biggestSoFar = t;
}
}
return biggestSoFar;
}
If the bridge method didn't exist and you passed a List<Integer> and a MyComparator to this function, the call at the line tagged IMPORTANT would fail since MyComparator would have no method called compare that takes two Objects...only one that takes two Integers.
The FAQ below is a good read.
See Also:
The Generics FAQ - What is a bridge method?
Java bridge methods explained (thanks #Bozho)
If you want to understand why you need bridge method, you better understand what happens without it. Suppose there is no bridge method.
class A<T>{
private T value;
public void set(T newVal){
value=newVal
}
}
class B extends A<String>{
public void set(String newVal){
System.out.println(newVal);
super.set(newVal);
}
}
Notice that after erasure, method set in A became public void set(Object newVal) since there is no bound on Type parameter T. There is no method in class B the signature of which is the same as set in A. So there is no override. Hence, when something like this happened:
A a=new B();
a.set("Hello World!");
Polymorphism won't work here. Remember you need to override the method of parent class in child class so that you can use parent class var to trigger polymorphism.
What bridge method does is silently override the method in parent class with all the information from a method with the same name but a different signature. With the help of the bridge method, polymorphism worked. Though on the surface, you override the parent class method with a method of different signature.
It's insteresting to note that the compiler infers that MyComparator's method:
public int compare(Integer a, Integer b) {/* code */}
is trying to override Comparator<T>'s
public int compare(T a, T b);
from the declared type Comparator<Integer>. Otherwise, MyComparator's compare would be treated by the compiler as an additional (overloading), and not overridding, method. And as such, would have no bridge method created for it.
As indicated by this article and this article, the key reason of the Java bridge method is Type Erasure and Polymorphism.
Let's take the class ArrayDeque (source code) as example, it contains a clone() method as bellow, because the class ArrayDeque implements the Cloneable interface so it must override the Object.clone() method.
public class ArrayDeque<E> extends AbstractCollection<E>
implements Deque<E>, Cloneable, Serializable
{
public ArrayDeque<E> clone() {
....
}
}
But the problem is the return type of ArrayDeque.clone() is ArrayDeque<E>, and it did not match to the method signature defined in the parent Object.clone(), and in Object.java the return type is Object instead.
public class Object {
protected native Object clone() throws CloneNotSupportedException;
}
The return type mismatch is a problem for Polymorphism. So in the compiled result file ArrayDeque.class, the Java compiler generated two clone() methods, one match the signature in the source code, the other one match to the signature in the parent class Object.clone().
clone() method returns ArrayDeque<E>, which is generated based on the corresponding source code
clone() method returns Object, which is generated based on Object.clone(). This method is doing nothing but calling the other clone() method. And, this method is tagged as ACC_BRIDGE, which indicates this method is generated by the compiler for the Bridge purpose.
Related
public interface Intf {
int size();
}
public class Cls1 implements Intf {
public int size() {
// implementation 1
}
public class Cls2 implements Intf {
public int size() {
// implementation 2
}
Now, which of the above two implementations will the following method reference refer to ?
Intf::size // note: using Intf
On what basis will the compiler choose between the above two? Or will this method reference throw an exception ?
The normal way that polymorphism works in general.
That is, the same way that if you have an intf1 someObject, you can call someObject.instanceMethodName() and it works.
This is no different for method references than any other kind of method invocation.
There will be no implementation selection in case you described (and it's a good thing, neither compiler nor interpreter can make that decision), because inferrable type of
Intf::size
is actually a
Function<? extends Intf, Integer>
or
ObjToIntFunction<? extends Intf> // considering that return type is primitive int
Which means that you create lambda that accepts a single instance assignable to Intf type and returns an integer number.
Now, if you say
new Cls1()::size
then you will produce an IntSupplier, or Supplier<Integer>, whichever's better suited. Suppliers being methods that accept no argument, require you to make implementation selection yourself.
I am trying to create following enum.
public enum MyEnum{
LEAD {
#Override
public boolean isValid(Lead lead) { //compile error, asks to retain type as T
}
},
TASK {
#Override
public boolean isValid(Task task) { //compile error, asks to retain type as T
}
};
public abstract <T extends SObject> boolean isValid(T object);
}
Lead and Task classes both extend SObject. My intention is to basically let clients be able to use MyEnum.LEAD.isValid(lead) or MyEnum.TASK.isValid(task). Compiler shouldn't allow to pass other types.
Could someone help in understand why this is happening.
Thanks
You need to override the generic method with the same generic method. If you want to do what you are asking you need a generic class - which an enum cannot be.
The point being that I refer to the enum by the class reference - i.e.
final MyEnum myenum = MyEnum.LEAD;
Now if I call myenum.isValid() I should be able to call it with any SObject as defined by your abstract method.
The generic method definition that you have doesn't actually do anything. All it is doing is capturing the type of the passed in SObject and storing it as T. A generic method is commonly used to tie together types of parameters, for example
<T> void compare(Collection<T> coll, Comparator<T> comparator);
Here we do not care what T actually is - all we require is that the Comparator can compare the things that are in the Collection.
What you are thinking of is a generic class, something like:
interface MyIface<T> {
boolean isValid(T object);
}
And then
class LeadValid implements MyIface<Lead> {
public boolean isValid(Lead object){}
}
You see the difference is that you would have a MyIface<Lead> - you would have to declare the type of MyIface. In the enum case you only have a MyEnum.
Reading the Javadoc for the #Override annotation, I came across the following rule:
If a method is annotated with this
annotation type compilers are required to generate an error message
unless at least one of the following conditions hold:
The method does override or implement a method declared in a supertype.
The method has a signature that is override-equivalent to that of any public method
declared in Object.
I'm clear on the first point, but I'm unsure about the second one.
What does it mean by "override-equivalent"? How are public methods of Object special in this respect? And why is this not covered under the first criterion?
Moreover, this is only true of the Java 7+ documentation. The Java 6 doc doesn't say anything about override-equivalence. Why the change?
Update:
After further consulting the JLS (Section 8.4.2), I found the following explanation of override-equivalence:
The signature of a method m1 is a subsignature of the signature of a method m2 if
either:
m2 has the same signature as m1, or
the signature of m1 is the same as the erasure (ยง4.6) of the signature of m2.
Two method signatures m1 and m2 are override-equivalent iff either m1 is a
subsignature of m2 or m2 is a subsignature of m1.
As far as I can tell, this answers the first question ("What does it mean?") and the third question ("Why doesn't the first condition cover this?").
If I understand correctly (please inform me if I don't!), there is only one case where two methods are override-equivalent and which doesn't fall under the first condition of the original question. This is the case when the erasure of the signature of the subclass method is the same as the signature of the superclass method, but not the other way around.
The second condition of the original question, then, would only come into play when we attempt to add type parameters when attempting to "override" a public method of the Object class. I tried the following simple example to test this, with an unused type parameter:
public class Foo {
#Override
public <T> boolean equals(Object obj) {
return true;
}
}
Of course, this class doesn't compile, because the method doesn't actually override the equals method and thus clashes with it. But I also still receive a compiler error for using the #Override annotation. Am I wrong in assuming that this example meets the second condition for #Override usage? Or is the compiler generating this error despite not being required to?
The reason for this is to allow you to use the #Override annotation in interfaces, which do not inherit from Object but implicitly declare all public methods from Object (see JLS section 9.2 interface members). You are thus allowed to declare an interface like:
interface Bar { #Override int hashCode(); }
However, you would not be allowed to declare the following interface:
interface Quux { #Override Object clone(); }
since the clone() method is not implicitly declared in an interface (it is not public).
This is described in JLS section 9.6.3.4 #Override (the Javadoc for #Override still refers to an old section number)
Your question is basically a design question and JLS explains its:
"The notion of subsignature is designed to express a relationship
between two methods whose signatures are not identical, but in which
one may override the other. Specifically, it allows a method whose
signature does not use generic types to override any generified
version of that method. This is important so that library designers
may freely generify methods independently of clients that define
subclasses or subinterfaces of the library."
Your code is not a valid example of this , see the below code it works:
public class SubSignatureTest extends SignatureTest {
#Override
public List test(Collection p) {
return null;
}
/**
* #param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
}
}
class SignatureTest {
public <T> List<T> test(Collection<T> t) {
return null;
}
}
Whole point is that signature of superclass and subclass should be same after erasure.
EDIT:
When we talk of override equivalence then parent class should have generic method and child class should have non generic method. Here is an example to explain this .Below code will not work because child class have generic method. For a moment lets assume that java allowed that then the call in main method will always fail :
class A{
public int compareTo(Object o){
return 0;
}
}
class B extends A implements Comparable<B>{
public int compareTo(B b){
return 0;
}
public static void main(String[] argv){
System.out.println(new B().compareTo(new Object()));
}
}
In class B method will be like this after compilation:
public int compareTo(Object x){
return compareTo((B)x);
}
Which means this is always error: new B().compareTo(new Object()) .
Therefore java will not allow child class to have generic method if parent class has non generic method. So you can't define override equivalence methods for object class.
Hope that clarifies.
I used the post http://lists.seas.upenn.edu/pipermail/types-list/2006/001091.html for reference, it has lot more details.
Given a few classes/interfaces.
public interface A {
public int doSomthing(int x);
}
public class B implements A {
int doSomthing(int x){
//actually do something
};
}
public class C extends B {
//does some specific implementations of what B does
// but does NOT override "int doSomething(int)"
}
How in a code using implementation C (or any subClass of C) may I determine (programatically) that B was the class implementing int doSomething(int).
Or if any of B's subclasses (lets say D which extends C) overrid "int doSomething(int)" how, when working with E (which extends D, yeah ... this is one large family of classes) may I define first parent that implemented "int doSomething(int)" ?
Thank you all in advance :)
You can do that using reflection, i.e. you start at the class the object has and check whether that class defines the method which is identified by the methodname and parameter types. If the class doesn't define that method you get its super class and check this, until you hit Object in which case the method isn't available at all.
For public methods, it's easier since Java has already a built-in method for this:
Class<?> mostSpecificImplementor =
yourObject.getClass().getMethod( "doSomthing", int.class ).getDeclaringClass();
Note that this only works for public methods, otherwise you'd have to search up the class hierarchy yourself (use getDeclaredMethod(...) in this case).
I'm trying to implement a sorted list as a simple exercise in Java. To make it generic I have an add(Comparable obj) so I can use it with any class that implements the Comparable interface.
But, when I use obj.compareTo(...) anywhere in the code I get "unchecked call to compareTo(T) as a member of the raw type java.lang.Comparable" from the compiler (with -Xlint:unchecked option). The code works just fine but I can't figure out how to get rid of that annoying message.
Any hints?
In essence, this warning says that Comparable object can't be compared to arbitrary objects. Comparable<T> is a generic interface, where type parameter T specifies the type of the object this object can be compared to.
So, in order to use Comparable<T> correctly, you need to make your sorted list generic, to express a constraint that your list stores objects that can be compared to each other, something like this:
public class SortedList<T extends Comparable<? super T>> {
public void add(T obj) { ... }
...
}
Using an interface like Comparable as a method parameter doesn't make your class generic, declaring and using generic type parameters is how you make it generic.
Quick-n-dirty answer: You are receiving the warning because you are using Comparable, which is a generic interface, as a raw type, rather than giving it a specific type arguments, like Comparable<String>.
To fix this, make add() generic by specifying type parameters:
<T extends Comparable<? super T>> add(T obj) { ... }
But this quick fix won't fix the general problem that your class is unsafe. After all, shouldn't all the objects in your list be of the same type? This add method lets you still different types into the same list. What happens when you try to compare heterogeneous types (how do you compareTo an Object instance to an Number instance, or to a String instance)? You can depend on the user of the class to do the right thing and ensure they only stick 1 kind of thing in your list, but a generic class will let the compiler enforce this rule.
The better approach: The proper fix is that your sorted list class should be probably be generic overall, just like the other collection classes in java.util.
You would probably like something like:
public class SortedList<T extends Comparable<? super T>>
implements Iterable<T> {
...
public void add(T item) { ... }
public Iterator<T> iterator() { ... }
...
}
Note that when the class is generic, the add method uses the classes formal type parameter rather than declaring its own formal type parameter.
There should be plenty of tutorials on the web on how to create a generic class, but here's a quick example:
http://www.angelikalanger.com/GenericsFAQ/FAQSections/ParameterizedTypes.html#FAQ002
class Pair<X,Y> {
private X first;
private Y second;
public Pair(X a1, Y a2) {
first = a1;
second = a2;
}
public X getFirst() { return first; }
public Y getSecond() { return second; }
public void setFirst(X arg) { first = arg; }
public void setSecond(Y arg) { second = arg; }
}
You need to "check" or define the Comparable object like so:
add(Comparable<Object> obj)