Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

java

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?

like image 283
userfault Avatar asked May 03 '18 15:05

userfault


People also ask

What are the two 2 types of Erasure?

- Erasure is a type of alteration in document. It can be classified as chemical erasure and physical erasure.

What is type erasure and explain the functionality of type erasure with example?

Type erasure is a process in which compiler replaces a generic parameter with actual class or bridge method. In type erasure, compiler ensures that no extra classes are created and there is no runtime overhead.

What is the point of type erasure?

Type erasure ensures that no new classes are created for parameterized types; consequently, generics incur no runtime overhead.

What is name clashing in Java?

At runtime, the parameter types are replaced by Object . So saveAll(Collection<?>) and saveAll(Collection<MyClass>) are transformed to saveAll(Collection) . This is a name clash.


2 Answers

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.

like image 139
Michael Berry Avatar answered Sep 29 '22 11:09

Michael Berry


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>.

like image 35
Sweeper Avatar answered Sep 29 '22 10:09

Sweeper