I was trying to fix a Problem with an unknown Comparator (no Source access).
So I've wrote some Reflection Code to see what Types that Comparator accepts.
Surprisingly, Reflection tells me, there are two compare-Methods, one with the real type and one with Object:
Comparator<Integer> comp = new Comparator<Integer>()
{
#Override
public int compare(Integer o1, Integer o2)
{
return 0;
}
};
Method[] methods = comp.getClass().getMethods();
for (Method method : methods)
{
if(method.getName().equals("compare")){
System.out.println(method);
}
}
Output:
public int de.hinneLinks.stackoverflow.MyClass$1.compare(java.lang.Integer,java.lang.Integer)
public int de.hinneLinks.stackoverflow.MyClass$1.compare(java.lang.Object,java.lang.Object)
Where is that second compare Method coming from?
But its not usable, why?:
comp.compare(1, 2); //Compiles
comp.compare((Object)1,(Object)2); //Does not Compile
I can however call those Methods with Reflection, if i call both Methods with new Object(), i'll get two different Exceptions:
compare(java.lang.Object,java.lang.Object)
java.lang.ClassCastException: java.lang.Object cannot be cast to java.lang.Integer
compare(java.lang.Integer,java.lang.Integer)
java.lang.IllegalArgumentException: argument type mismatch
If define my Comparator with Object, then there is only one Method.
The other method (compare(Object obj1, Object2) is a bridge method generated by the compiler to preserve binary compatibility after type erasure:
When compiling a class or interface that extends a parameterized class or implements a parameterized interface, the compiler may need to create a synthetic method, called a bridge method, as part of the type erasure process. You normally don't need to worry about bridge methods, but you might be puzzled if one appears in a stack trace.
You can add a check on the method to see if it's a bridge method:
for (Method method : methods) {
if (method.getName().equals("compare") && !method.isBridge()) {
System.out.println(method);
}
}
This is due to Type Erasure, which was a design decision (preserving backwards-compatibility) in the implementation of Generics in Java.
During the type erasure process, the Java compiler erases all type parameters and replaces each with its first bound if the type parameter is bounded, or Object if the type parameter is unbounded.
This allows your class to be accessed from pre-Java-5 JVMs, where Comparator<Integer> isn't visible but Comparator is (where it supplies compare(Object, Object)). The implementation of compare(Object, Object) simply casts each argument to an Integer and calls compare(Integer, Integer), which is why you get your exceptions.
Related
I have these 3 classes
public class Box<O> {
public O getItem() {...}
}
public class CoolBox extends Box<Integer> { ... }
public class AmazingBox extends CoolBox { ... }
At one point in my code, I need to get the return Type of the method getItem() for the AmazingBox class, but when accessing its methods via reflection, I get Object as the return Type instead of Integer.
Is there any way, (plain java or extra libraries) to get Integer as the return Type?
This is the code I used to get the return type:
Class<?> c = AmazingBox.class; // This is not how i get the class but its for demonstration purposes
Method m = c.getMethod("getItem");
Type t = m.getReturnType();
I just discovered that there is a library called GenericsResolver that does exactly what I want.
Using this piece of code it returns the correct type
Class<?> clazz = AmazingBox.class;
GenericsContext genericsContext = GenericsResolver.resolve(clazz);
Method method = clazz.getMethod("getItem");
Type methodReturnType = genericsContext.method(method).resolveReturnType();
While inheriting you can use generics for inheriting classes and create lower or upper bounds accordingly. If you don't do that assume you are working without using generics and won't get type safety on inheriting non-generic classes.
In short, if you go hybrid, type erasures will become effective with following rules (ref- oracle documentation)
Type Erasure
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. The produced bytecode, therefore, contains only ordinary classes, interfaces, and methods.
Insert type casts if necessary to preserve type safety.
Generate bridge methods to preserve polymorphism in extended generic types.
Type erasure ensures that no new classes are created for parameterized types; consequently, generics incur no runtime overhead.
I have learned that in compile time generics we use changes to most specific type. so what is the benefit of generics?
for example in this code:
int a=1;
double b = 2.1;
printArray(a,b);
public static <E> void printArray(E first , E second);
in compile time E changes to Number (because the specific parent of int and double is Number)
so what is the benefit of generics when we can write Number instead of E in here?
The purpose of generics is not related to the fact that the generic parameter will be inferred to be the most specific type. The purpose of generic methods is to allow you to write a method that can work with any type and at the same time, maintain type safety.
Your printArray method can not only take Number as arguments, but also String, SomeClass, Object and all sorts of reference types.
At this point you might ask "can't we just write Object as the parameter types?" Sure with the type being Object, the parameter will not only accept all reference types, but the primitives will also be accepted and boxed! So in this specific case, you don't necessarily have to use a generic method.
However, there are times when generic methods shine.
One of these occasions is when the methods work with generic types. For example, this is Optional.ofNullable:
public static <T> Optional<T> ofNullable(T value) {
return value == null ? empty() : of(value);
}
It creates a Nullable<T> depending on whether the parameter is null. If this were not generic, it would have to return Optional<Object>. Every time you want to call get or orElse, you need to cast the result to the type you want. If you made a mistake in one of the casts, a ClassCastException might be thrown at runtime. But with generics, the compiler checks the types for you!
Here's another example:
public static <T> T first(List<T> list) { return list.get(0); }
Without generics, you would have to use the raw type List and return an Object. This again creates the problems of casting and reduced type safety.
While studying Java Generics, I was wondering if it's possible to declare a method that receives two arguments, the first of which is of generic type T, and the second one is of generic type S, which extends (or is the same type as) T. So I started to experiment on this, and wrote the following method declaration:
public static <T, S extends T> void testMethod(T t, S s)
{
System.out.println("We are inside testMethod.");
}
and then I called this method in two ways as follows:
java.lang.Integer num1 = new java.lang.Integer(0);
java.lang.Integer num2 = new java.lang.Integer(1);
testMethod(num1, num2);
testMethod(num1, new Object());
expecting the second call to cause a compile-time exception. But the program was compiled and run with no errors. Next, I changed the method testMethod declaration as follows:
public <T extends java.lang.Number, S extends T> void testMethod(T t, S s)
with the following calls:
testMethod(new Integer(0), new Double(1.1));
testMethod(new Integer(0), new Object());
The first call is error free, but the second one causes a compile-time error. It seems that as long as t and s are both subtypes of java.lang.Number, no error occurs, whether s is a subtype of t or not.
My question: How can we declare a method that receives two arguments, one of which is guaranteed to be a subtype of the other?
There are two problems:
In terms of generics, any type is a subtype of itself. There's no way to declare S extends T such that S and T can't be the same type. Furthermore, even if it were possible, it wouldn't have much of an effect here, because any instance of S could also be passed as T.
Type inference does its best to make the method compile. If S is not compatible with the type of the first argument, the compiler will infer the nearest common ancestor as T. You can avoid this issue by explicitly specifying T when you call the method:
ThisClass.<Integer, Integer>testMethod(1, 2); // compiles
ThisClass.<Integer, Integer>testMethod(1, 2.0); // does not compile
The fact is that, ultimately, the method itself can't enforce the requirement that S is a subtype of (or even compatible with) t's type. I don't think there's any way to work around that.
In version 1.5, Java have introduced the concept of auto-boxing.
public interface SomeInterface {
public void test(Integer val);
}
public class Main implements SomeInterface {
/*The method signature gets different and compiler is asking to override
un-implemented methods*/
public void test(int t) {
}
}
Then why I am getting compile time error for overriding un-implemented methods, why above test method's arguments are not auto-boxed to match parent test method signature?
It's because the method in subclass is not override-equivalent with the one in super class. The super class method can take null as argument, while the subclass method can't (There's nothing about auto-boxing here).
Because Integer Not Equal to int
Integer is class
int is primitive type
So both methods have different argument types that's why you are not overriding the method but creating newer one in your class.
You can call method with autoboxing feature but you can not ovverride.
Overriding
An instance method in a subclass with the same signature (name, plus
the number and the TYPE of its parameters) and return type ....
The Java compiler applies autoboxing when a primitive value is:
Passed as a parameter to a method that expects an object of the corresponding wrapper class.
Assigned to a variable of the corresponding wrapper class.
Autoboxing and unboxing can happen anywhere where an object is expected and primitive type is available for example In method invocation where an object argument is expected, if you pass primitive, Java automatically converts primitive into equal value.
Read more: http://javarevisited.blogspot.com/2012/07/auto-boxing-and-unboxing-in-java-be.html#ixzz36Rhg91CB.
and here your class Main implements interface SomeInterface and this is the implementation of method not the calling of a method where autoboxing works.
This is conceptually impossible, because the Java runtime does not know a thing about boxing. The boxing feature was implemented on the Java compiler level, similarly to generics. As a matter of fact, the Java runtime does not consider a primitive int and a boxed Integer to have anything in common, even in the current Java 8 version.
Instead, whenever you make an assignment like:
Integer i = 42;
the Java compiler desugars this expression to
Integer i = new Integer(42);
which is then observed as above by the Java runtime. At the same time, the Java runtime considers
void foo(int i);
void foo(Integer i);
to be different methods which is why boxing would have been difficult to be implemented at the runtime level for reasons of backwards compatibility.
The simple answer to this is :
If 2 methods can be overloaded, it means, compiler considers the signatures to be different.
And, for Overriding a method, both the methods should be having the same signature.
E.g. :
//Method overloading, compiler doesn't complain, which means "signatures are different",
//because one is expecting an object of Integer Class, and other is expecting a primitive value.
class A{
public void a(Integer a){...}
public void b(int a){...}
}
//Method Overriding, compiler complains, because "signatures are different!"
class A{
public void a(Integer a){...}
}
class B extends A{
#Override
public void a(int a){...}
}
During navigation of the java.lang.reflect.Method class I came across the method isBridge. Its Javadoc says that it returns true only if the Java spec declares the method as true.
Please help me understand what this is used for! Can a custom class declare its method as a bridge if required?
A bridge method may be created by the compiler when extending a parameterized type whose methods have parameterized arguments.
You can find in this class BridgeMethodResolver a way to get the actual Method referred by a 'bridge method'.
See Create Frame, Synchronize, Transfer Control:
As an example of such a situation, consider the declarations:
class C<T> { abstract T id(T x); }
class D extends C<String> { String id(String x) { return x; } }
Now, given an invocation
C c = new D();
c.id(new Object()); // fails with a ClassCastException
The erasure of the actual method being invoked, D.id(String) differs in its signature from that of the compile-time method declaration, C.id(Object). The former takes an argument of type String while the latter takes an argument of type Object. The invocation fails with a ClassCastException before the body of the method is executed.
Such situations can only arise if the program gives rise to an unchecked warning (ยง5.1.9).
Implementations can enforce these semantics by creating bridge methods. In the above example, the following bridge method would be created in class D:
Object id(Object x) { return id((String) x); }
This is the method that would actually be invoked by the Java virtual machine in response to the call c.id(new Object()) shown above, and it will execute the cast and fail, as required.
See also Bridge:
as mentioned in the comment, bridge methods are also needed for covariant overriding:
In Java 1.4, and earlier, one method can override another if the signatures match
exactly.
In Java 5, a method can override another if the arguments match exactly but the return type of the overriding method, if it is a subtype of the return type of the other method.
Typically, a method Object clone() can be overridden by a MyObject clone(), but a bridge method will be generated by the compiler:
public bridge Object MyObject.clone();
The example shown there (quoted from the JLS) makes it sound like bridge methods are only used in situations where raw types are used. Since this is not the case, I thought I'd pipe in with an example where bridge methods are used for totally type-correct generic code.
Consider the following interface and function:
public static interface Function<A,R> {
public R apply (A arg);
}
public static <A, R> R applyFunc (Function<A,R> func, A arg) {
return func.apply(arg);
}
If you use this code in the following way, a bridge method is used:
Function<String, String> lower = new Function<String, String>() {
public String apply (String arg) {
return arg.toLowerCase();
}
};
applyFunc(lower, "Hello");
After erasure, the Function interface contains the method apply(Object)Object (which you can confirm by decompiling the bytecode). Naturally, if you look at the decompiled code for applyFunc you'll see that it contains a call to apply(Object)Object. Object is the upper bound of its type variables, so no other signature would make sense.
So when an anonymous class is created with the method apply(String)String, it doesn't actually implement the Function interface unless a bridge method is created. The bridge method allows all generically typed code to make use of that Function implementation.
Interestingly, only if the class implemented some other interface with the signature apply(String)String and only if the method was called via a reference of that interface type would the compiler ever emit a call with that signature.
Even if I have the following code:
Function<String, String> lower = ...;
lower.apply("Hello");
The compiler still emits a call to apply(Object)Object.
There is actually one other way to get the compiler to call apply(String)String, but it takes advantage of the magical type assigned to an anonymous class creation expression which cannot otherwise be written down:
new Function<String, String>() {
public String apply (String arg) {
return arg.toLowerCase();
}
}.apply("Hello");
Another case I stumbled across has nothing to do with generics:
protected abstract class Super {
public void m() {}
}
public class Sub extends Super {}
assert Sub.class.getMethod("m").isBridge();