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.
Bridge Method: These are methods that create an intermediate layer between the source and the target functions. It is usually used as part of the type erasure process. It means that the bridge method is required as a type safe interface.
3. Types of Type Erasure. Type erasure can occur at class (or variable) and method levels.
Generics were introduced to the Java language to provide tighter type checks at compile time and to support generic programming. To implement generics, the Java compiler applies type erasure to: Replace all type parameters in generic types with their bounds or Object if the type parameters are unbounded.
Generics are checked at compile-time for type-correctness. The generic type information is then removed in a process called type erasure. For example, List<Integer> will be converted to the non-generic type List , which ordinarily contains arbitrary objects.
Generic Method: Generic Java method takes a parameter and returns some value after performing a task. It is exactly like a normal function, however, a generic method has type parameters that are cited by actual type.
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.
There are some fundamental differences between the two approaches to generic types. Generic Method: Generic Java method takes a parameter and returns some value after performing a task. It is exactly like a normal function, however, a generic method has type parameters that are cited by actual type.
To create objects of generic class, we use following syntax. // To create an instance of generic class BaseType <Type> obj = new BaseType <Type> () Note: In Parameter type we can not use primitives like 'int','char' or 'double'. We can also pass multiple Type parameters in Generic classes.
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 Object
s 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);
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 Object
s...only one that takes two Integer
s.
The FAQ below is a good read.
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.
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