Type erasure and overriding generic method - java

My understanding about generic was that if I have public void saveAll(Collection<? extends Object> stuff) { then compile will determine the "most common type" of the input parameter to be Collection<Object>. Now, with that I wrote below code for overriding the generic methods, but I get following error message in the overriding method - Name clash: The method saveAll(Collection<? extends Object>) of type ChildWithGenericMethod has the same erasure as saveAll(Collection<Object>) of type ParentWithGenericMethod<T> but does not override it
public class ParentWithGenericMethod {
public void saveAll(Collection<Object> stuff) {
}
}
public class ChildWithGenericMethod extends ParentWithGenericMethod{
public void saveAll(Collection<? extends Object> stuff) {
}
}
What I want to know is that after type erasure how these methods would look like, I thought they would look like public void saveAll(Collection stuff) { and hence I should be able to override, because if I don't use generics then I can override like this.
Please note that I know that I can see the byte code after compilation using "javap" but it doesn't tell me how these methods would look like after "type erasure".
Update:
This question doesn't answer my question, if after type erasure both methods become public void saveAll(Collection stuff) { then why subclass is getting overriding error, because with public void saveAll(Collection<Object> stuff) { it passes the overriding rules.

It's worth remembering run time vs compile-time dispatch.
Take away generics for a second.
public class Parent {
public void saveAll(Object x) {
}
}
public class Child extends Parent {
public void saveAll(String x) {
}
}
public String x = "hello";
public Object y = new Integer(334);
public Object z = "goodbye";
public Child c1 = new Child();
c1.saveAll(x); // invokes Child.saveAll
c1.saveAll(y); // invokes Parent.saveAll
c1.saveAll(z); // invokes Parent.saveAll even though z contains a String
Now put generics back into the story.
public class Child<T extends Object> extends Parent {
public void saveAll(T x) {
}
}
Here, saveAll overloads, rather than overrides, the parent's saveAll.
replacing T with an anoymous ? doesn't really change that -- the compiler is trying to create a method that overloads the parent's method, but then realizes that it can't dispatch on compile-time type.
[edit, see Lew Block's comment on the original: the compiler needs to decide whether the child method overrides or overloads the parent's method before type erasure]

if after type erasure both methods become public void saveAll(Collection stuff) { then why subclass is getting overriding error, because with public void saveAll(Collection stuff) { it passes the overriding rules.
The problem (again see Lew Bloch's comment) is that overriding is determined before type erasure. Before type erasure, the parent and child method have different signatures, and so the compiler creates them as overloaded methods rather than overriding methods. After type erasure, the compiler realizes it is in a bind because it now has two methods with the same signature and no way to know how to dispatch.

Related

trying to understand case of "both methods have same erasure, yet neither overrides the other" [duplicate]

This question already has answers here:
Method has the same erasure as another method in type
(8 answers)
Closed 4 years ago.
I'm trying to solve a problem and I've come up with this solution (simplified):
package help;
public class Problem {
private static class A<T> {
public void foo(T t) {}
}
private static class B<T> {}
private static class C<T> extends A<B<T>> {
public void foo(T t) {}
}
}
It wont compile since "foo(T) in help.Problem.C clashes with foo(T) in help.Problem.A; both methods have same erasure, yet neither overrides the other".
I'm not just trying to solve the problem, i also would like to understand what is going on. I noticed that if the B class is omitted, the error is gone.
Also: could you provide an example of a piece of code such, that the compiler wouldn't be able to bind a variable to one of those two methods?
The class definition of A means that the compiler "expects" something like this if you want to override foo:
private static class C<T> extends A<B<T>> {
public void foo(B<T> t) {}
}
However, since you're just providing T as a type to that method rather than B<T>, it's not a valid override of foo in class A - the parameter types must match. This wouldn't cause an error with non-generic types - you've just got an overloaded method (a method that has the same name, but differs in the type / number of its parameters) instead of an overriden one.
However, since the generic types disappear on compilation (through type erasure), this means you can't have two methods differentiated only by an generic type, as their definitions in the bytecode would be identical.
To see why they erase to the same type, just remove all <> and the stuff inside them, then replace all the generic parameter types with Object:
class Problem {
private static class A {
public void foo(Object t) {}
}
private static class B {}
private static class C extends A {
public void foo(Object t) {}
}
}
Now you see why.
Not being able to overload a method like this is one of the limitations of generics. Read more here.
The root of the problem is really that in C, T means something different than it does in A. Let's suppose you remove the declaration of foo in C and you have an instance of C<T>. You want to call foo using this instance. What parameter do you need to pass? A B<T>, right? If foo is declared to accept a T in C, that wouldn't override A.foo because A.foo need to accept a B<T>, not just T.
To actually override foo, change the parameter to be of type B<T>.

Using Java and C#'s generics to simulate duck typing

http://nullprogram.com/blog/2014/04/01/ tries to explain that Java's generics can't simulate duck typing with an example:
class Caller<T> {
final T callee;
Caller(T callee) {
this.callee = callee;
}
public void go() {
callee.call(); // compiler error: cannot find symbol call
}
}
class Foo {
public void call() { System.out.print("Foo"); }
}
class Bar {
public void call() { System.out.print("Bar"); }
}
public class Main {
public static void main(String args[]) {
Caller<Foo> f = new Caller<>(new Foo());
Caller<Bar> b = new Caller<>(new Bar());
f.go();
b.go();
System.out.println();
}
}
The program will fail with a compile-time error. This is the result of
type erasure. Unlike C++’s templates, there will only ever be one
compiled version of Caller, and T will become Object. Since Object has
no call() method, compilation fails.
Does it mean that by Java generics, the methods of a type parameter are limited to the methods of class java.lang.Object?
C#'s generics is implemented in terms of reification instead of type erasure. Does C#'s generics not have the above limitation as Java's generics? So can C#'s generics actually achieve the same thing as duck typing?
Thanks.
can C#'s generics actually achieve the same thing as duck typing?
No. But C#'s generics can include a constraint where the type parameter is restricted to inherit or implement some particular type. When that's done, any expression of the type of that type parameter is resolved as the constrained type and members from that type can be accessed.
This is similar to the extends constraint described in the article you read.
The only duck-typing support in C# is the dynamic keyword, where final compilation of expressions involving dynamic values is deferred until runtime when the actual runtime type is known.
Related reading:
Trivial C# class with a generic parameter wouldn't compile for no apparent reason
Call a method of type parameter
Does it mean that by Java generics, the methods of a type parameter are limited to the methods of class java.lang.Object?
Not exactly. While most generics are erased, it is possible to include a constraint in Java such that the type parameter must be of a certain type. Which effectively makes it not Object.
Untested, but this should be close.
class Caller<T extends CallMe> {
final T callee;
Caller(T callee) {
this.callee = callee;
}
public void go() {
callee.call(); // should work now
}
}
interface CallMe {
void call();
}
class Foo implements CallMe {
public void call() { System.out.print("Foo"); }
}
class Bar implements CallMe {
public void call() { System.out.print("Bar"); }
}
public class Main {
public static void main(String args[]) {
Caller<Foo> f = new Caller<>(new Foo());
Caller<Bar> b = new Caller<>(new Bar());
f.go();
b.go();
System.out.println();
}
}
C# has the same limitations.
Other than dynamic, C# does not have arbitrary ducky-typing anywhere; even with generics, you can only call methods as defined by a type (specifically, the constraint(s) for the generic type parameter, which default to object).

How Java Picks an Overloaded Method in Polymorphism

I have two classes defined as:
public static class Mammal extends Animal {
public void info(String s) {
System.out.print("Mammal");
}
}
public static class Animal {
public void info(Object o) {
System.out.print("Animal");
}
}
Now I define two variables as:
Mammal m = new Mammal();
Animal a = new Mammal();
When I call m.info("test"), it will print
"Mammal"
When I call a.info("test"), it will print
"Animal"
Why does the Mammal type call the Mammal info() method and the Animal type call the Animal info() method when they are both Mammal objects?
You're overloading the method, not overriding it.
Overloads are chosen at compile time, and when the compile-time type of a is Animal, then a call to a.info("test") can only resolve to the info(Object) method in Animal.
If you want to override the method, you need to use the same signature - specify the #Override annotation to get the compiler to check that for you:
public static class Mammal extends Animal {
#Override public void info(Object s) {
System.out.print("Mammal");
}
}
Now it will print "Mammal" in both cases, because the implementation of the method is chosen at execution-time based on the actual type of the object. Remember the difference between them:
Overloading: takes place at compile time, chooses the method signature
Overriding: takes place at execution time, chooses the implementation of that signature
I also note that you're using nested classes (as otherwise the static modifier would be invalid). That doesn't change the behaviour here, but it does make other changes to the features of classes. I strongly suggest that you try to work with top-level classes most of the time, only using nested types when there's a really compelling reason to do so.
Because
public void info(Object o) {
System.out.print("Animal");
}
doesn't overrode
public void info(String s) {
System.out.print("Mammal");
}
Their signatures are different. I.e one is Object the other - String.

Two methods when implementing interface containing only one

I created interface TwoMethods. Source code:
interface TwoMethods<T>
{
public void method(T t);
}
Then I created class implementing this interface and after disassembling I saw 2 methods.
Class:
class A implements TwoMethods<A>
{
#Override
public void method(A a) {}
}
After disassembling:
class A implements TwoMethods<A> {
A();
public void method(A); //first
public void method(java.lang.Object); //second
}
Similarly for Comparable interface. Why when I create parametrized interface I have 2 methods. It is always, when I use parameter? I have additionally method with Object as argument?
method(java.lang.Object) is called the bridge method and it's generated because of type-erasure at compile time.
See Effects of Type Erasure and Bridge Methods
If we look at interface TwoMethods bytecode we will see that the actual method is
public abstract method(Ljava/lang/Object;)V
that is at bytecode level information about type parameter does not exist, type is erased, JVM simply does not know about generics, type parameters are replaced either with Object or in case if T extends X with X. So from the point of view of JVM
class A implements TwoMethods<A> {
public void method(A a) {
...
method(A a) does not override interface method because in bytecode it is in method(Object obj) can override it. To fix this problem compiler builds an implicit method, so called bridge method, in class A
public void method(Object obj) {
method((A)obj);
}
visible in bytecode only. Now for this code
A a = new A();
TwoMethods<A> tm = a;
tm.method(a);
compiler will replace tm.method(a) with call to the bridge
INVOKEINTERFACE test/TwoMethods.method(Ljava/lang/Object;)V
and this will redirect the call to A.method(A a);

Java generic method inheritance and override rules

I have an abstract class that has a generic method and I want to override the generic method by substituting specific types for the generic parameter. So in pseudo-code I have the following:
public abstract class GetAndParse {
public SomeClass var;
public abstract <T extends AnotherClass> void getAndParse(T... args);
}
public class Implementor extends GetAndParse {
// some field declarations
// some method declarations
#Override
public <SpecificClass> void getAndParse(SpecificClass... args) {
// method body making use of args
}
}
But for some reason I'm not allowed to do this? Am I making some kind of syntax error or is this kind of inheritance and overriding not allowed? Specifically I'm getting an error about #Override because the eclipse IDE keeps reminding me to implement getAndParse.
Here's how I want the above code to work. Somewhere else in my code there is a method that expects instances of objects that implement GetAndParse which specifically means that they have a getAndParse method that I can use. When I call getAndParse on that instance the compiler checks to see whether I have used specific instances of T in the proper way, so in particular T should extend AnotherClass and it should be SpecificClass.
What we are having here is two different methods with individual type parameters each.
public abstract <T extends AnotherClass> void getAndParse(Args... args);
This is a method with a type parameter named T, and bounded by AnotherClass, meaning each subtype of AnotherClass is allowed as a type parameter.
public <SpecificClass> void getAndParse(Args... args)
This is a method with a type parameter named SpecificClass, bounded by Object (meaning each type is allowed as a type parameter). Do you really want this?
Is the type parameter used inside Args? I think the problem would be there.
The meaning of
public abstract <T extends AnotherClass> void getAndParse(T... args);
is that the caller of the method can decide with which type parameter he wants to call the method, as long as this is some subtype of AnotherClass. This means that in effect the method can be called with any objects of type AnotherClass.
Since the caller can decide the type parameter, you can't in a subclass narrow down the parameter type to SpecificClass - this would not be an implementation of the method, but another method with same name (overloading).
Maybe you want something like this:
public abstract class GetAndParse<T extends AnotherClass> {
public SomeClass var;
public abstract void getAndParse(T... args);
}
public class Implementor extends GetAndParse<SpecificClass> {
// some field declarations
// some method declarations
#Override
public void getAndParse(SpecificClass... args) {
// method body making use of args
}
}
Now the getAndParse method implements the parent class' method.
You are seeing this problem because of the concept called "Erasure" in Java Generics.
Java uses "erasure" to support backward compatibility. i.e Java code which did not use generics.
Erasure Procedure:
The compiler will first do a type checking and then it will remove(erase) all the type parameters as much as possible, and also insert TypeCasting where ever necessary.
example:
public abstract <T extends AnotherClass> void getAndParse(T paramAnotherClass);
will become
public abstract void getAndParse(AnotherClass paramAnotherClass);
In class "Implementor.java",
The code
public <SpecificClass> void getAndParse(T paramAnotherClass)
will become
public void getAndParse(SpecificClass paramAnotherClass){ }
the compiler will see that you have not implemented the abstract method correctly.
There is a type mismatch between the abstract method and the implemented method. This is why you are seeing the error.
More details can be found here.
http://today.java.net/pub/a/today/2003/12/02/explorations.html
You cannot override to specific type T because there is in fact (at the bytecode level if you wish) only one method getAndParse because of type erasure (see other answer):
public abstract void getAndParse(AnotherClass... args); // (1)
For every type of T, the same method is used.
You can overload it (I think):
public void getAndParse(SpecificClass... args); // (2)
but this will not a different method from (1) ant it will not be called by generic code:
T x = whatever;
object.getAndParse(x); // Calls (1) even if T is derived from SpecificClass
No, it's not valid. What would happen if someone with a GetAndParse reference called it with a different class extending AnotherClass?
That becomes a nonsense when someone has a reference to type GetAndParse and tries to call the getAndParse method. If Cat and Dog extend AnotherClass. I should expect to be able to call GetAndParse#getAndParse with either a Cat or a Dog. But the implementation has tried to restrict it and make it less compatible!
Static method can't override
class Vehicle{
static void park(int location){
System.out.println("Vehicle parking..");
}}
class Car extends Vehicle{
#Override //error
void park(int location) { //error
System.out.println("Car Parking..");
}}
Private method can't override
class Vehicle{
private void park(int location){
System.out.println("Vehicle parking..");
}
void callPark(){
park(100);
}}
class Car extends Vehicle{
//#Override
void park(int location) {
System.out.println("Car Parking..");
}}
class Demo {
public static void main(String[] args) {
Vehicle v1=new Car();
v1.callPark();
}}
Final method can't override
class Vehicle{
final void park(int location){
System.out.println("Vehicle parking..");
}}
class Car extends Vehicle{
//#Override
void park(int location) { //error
System.out.println("Car Parking..");
}}

Categories