Java Erasure with Generic Overloading (Not Overriding)

Java erasure with generic overloading (not overriding)

Either rename the methods, or use polymorphism: use an interface, and then either put the clawback code in the objects themselves, or use double-dispatch (depending on your design paradigm and taste).

With code in objects that would be:

public interface Clawbackable{
void clawBack()
}

public class CommissionFacade
{

public <T extends Clawbackable> void clawBack(Collection<T> objects)
{
for(T object: objects)
{
object.clawBack();
}
}
}

public class CommissionTrns implements Clawbackable {

public void clawback(){
// do clawback for commissions
}
}

public class FinanceRequest implements Clawbackable {

public void clawBack(){
// do clwaback for FinanceRequest
}

}

I prefer this approach, since I'm of the belief your domain should contain your logic; but I'm not fully aware of your exact wishes, so I'll leave it up to you.

With a double dispatch, you would pass the "ClawbackHandler" to the clawback method, and on the handler call the appropriate method depending on the type.

overriding / overloading methods with type erasure in java

The error "overloading with same signature" seems adequate here. It is not technically overriding because to the compiler generics matter and B's f does not satisfies A's signature as A's f T is not restricted to implement Cell<?> so B's f is not overriding A's f; it makes more sense to call it "overloading" instead because generic-wise those signatures are different despite end up being the same after erasure.

I reckon that the "overloading with same signature" error was added for this particular scenario with generics resulting in conflicting erasures.

Overloading Methods in Java with generics

You cannot have two methods with the same erasure in the same type. That said, you may be able to manipulate the erasure retaining the same type. For instance, if Foo was an interface:

interface Foo<T> { /* ... */ }

class Bar {
public void do_(Foo<Integer> obj) { }
public <A extends Object & Foo<Double>> void do_(A obj) { }
}

But, avoid overloading methods even where generics are not involved.

Type erasure prevents overloading, but allows overriding

The compiler removes generic classes at compile time. It will replace the place holder with it's restricted class.

In this case Base will replace any instance of A with Object and Sub will replace any instance of B with Object.

this gives the conflicting methods

in Base public boolean removeArc(Object arc);

and in Sub public Arc removeArc(Object value);

if however you did

public interface Base<A extends Arc<?>> {
public boolean removeArc(A arc);
}

then the instances of A would be replaced with Arc and the method signatures would no longer conflict.

Overloading a method: both methods have same erasure

What should I do to achieve method overloading, without raising an error?

Simple: don't try to overload the method with parameters with the same erasure.

A few options:

  1. Just give the methods different names (i.e. don't try to use overloading)
  2. Add further parameters to one of the overloads, to allow disambiguation (ideally only do this if you actually need those parameters; but there are examples in the Java API where there are junk parameters simply to avoid overloading issues).
  3. Bound the type variable, as suggested by @Kayaman:

    <V extends SomethingOtherThanObject>

Overriding a method using type erasure

At a simple guess the compiler does not use the generic view when calculating overloads which of course would not make sense, because sometimes T might be Object other times its another type. The overridding would then become dependent on a moving target T which is downright wrong, especially if there were multiple methods all called "get" but with different single parameter types. In such a case it just wouldnt make sense and at a guess they chose to just keep things simple.

Both methods have same erasure yet neither overrides the other

From JLS 8.4.2:

Two methods or constructors, M and N, have the same signature if they have the same name, the same type parameters (if any) (§8.4.4), and, after adapting the formal parameter types of N to the type parameters of M, the same formal parameter types.

How is that relevant to your code?

  1. The first method signature you posted is:

    public void func(String str, List<String> lst)

    This signature exists like this only at compile-time because it uses generics. Once compiled, the runtime signature will be like below (List<String> becomes List) due to type erasure:

    public void func(String str, List lst)
  2. The second method signature in your example is:

    public void func(String str, List<Integer> lst)

    While the starting point is different – a List of Integer instead of a List of String – the runtime signature for this sees a similar change, replacing List<Integer> with List:

    public void func(String str, List lst)

Even though your code specifies differences in the argument list (List<String> vs List<Integer>) which leads you to believe there are two different method signature, the two signatures are effectively the same:

  • they are both named "func", and
  • they have the same argument list: String, List

In Erasure of Generic Types, they show an example similar to what you're seeing where a type of Node<T> becomes Node. That is happening with your examples, too, where List<String> becomes List.

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.

Note that the return type is not part of a method signature uniqueness, which is why changing one to return Object didn't make a difference.

Here's a bit more info from the Java Tutorial section on Type Erasure, with a little emphasis added in a few spots:

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.

Method overloading with type erasure

This is a mistake in the text book. It has been acknowledged by the authors and (according to the errata page) corrected:

  • http://www.oreilly.com/catalog/errata.csp?isbn=9780596527754

UPDATE - actually, I'm not sure that I picked the right erratum. The page numbers (page 50) don't match with the one that you are talking about. But if you flip to the "unconfirmed errata" tab, there is already one for page 56.

Anyway, the meta-point remains. Reputable textbook publishers maintain errata pages, and they are a good place to look if you need clarification of an apparent mistake.

Type erasure, overriding and generics

The signature of fooMethod(Class<?>) is the same as the signature of fooMethod(Class) after erasure, since the erasure of Class<?> is simply Class (JLS 4.6). Hence, fooMethod(Class) is a subsignature of the fooMethod(Class<?>) but not the opposite (JLS 8.4.2).

For overriding with instance methods you need the overriding method to be a subsignature of the overridden method (JLS 8.4.8.1). This is clearly not the case here.

Now that we have established the fact that your subclass method doesn't override the superclass method according to the JLS, let's look at the runtime implications when type erasure has occured. We now have two methods that look exactly the 'same' (same name, same parameter types) but do not override each other. If they don't override, they must be both available on the subtype as separate methods, but they have identical runtime signatures: conflict. So Java has to disallow it.

Overriding generic parameter types using raw parameter types is allowed because raw types exist just for this reason: they are a convenient mechanism with specific unsound type rules to accommodate interaction with legacy code. So the type system here will decide that the subclass method does override the superclass one, they are identical after type erasure and we can never have a conflict. As a consequence of this libraries can be generified independently of existing non-generic code.



Related Topics



Leave a reply



Submit