Overriding a method using type erasure - java

Today I stumbled upon something interesting.
Assume the following Java 6 class:
public class Ereasure {
public Object get(Object o) {
return null; // dummy
}
public static class Derived<T> extends Ereasure{
// (1)
#Override
public Object get(T o) {
return super.get(o);
}
// (2)
/*
#Override
public Object get(Object o) {
return super.get(o);
}*/
}
}
If you try to compile the above example, the compiler says
Ereasure.java:9: method does not override or implement a method from a supertype
#Override
If you remove the #Override annotation(which should not be necessary!), it says
Ereasure.java:8: name clash: get(T) in Ereasure.Derived and get(java.lang.Object) in Ereasure have the same erasure, yet neither overrides the other
This is a bit contradictional, since T should erease to Object and therefor override the parent classes get method.
If you leave (1) unannotated and uncomment (2) so (1) overloads (2) it would not work either.
Compiler output:
Ereasure.java:15: get(T) is already defined in Ereasure.Derived
public Object get(Object o) {
As a conclusion, T is being ereased to Object, but cannot override the parent get Method.
My question is now, why dooesn't at least one of the examples compile?

You can see in the example below why it is impossible to do what you want:
public class Erasure {
public void set(Object o) {
return;
}
// method overloading: (which is valid)
public void set(String s) {
return;
}
public static class Derived<S> extends Erasure {
// Oops... which one am I supposed to override?
// (It would actually be overloading if S was a concrete type
// that is neither Object nor String.)
#Override
public void set(S o) { // does not compile
super.set(o);
}
}
}
The solution to your problem is that Erasure should be a parameterized class.

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.

Consider a case where you have both a getter and a setter overridden as generics.
Derived<String> d = new Derived<String();
Erasure e = d;
e.set(new Object());
String s = d.get(); //Class cast exception
The fundamental principal of generics is that a class cast exception can only happen if there is either (a) an explicit cast or (b) a warning. If you were allowed to do what you wanted, the above would throw an exception without either.

Related

why implementing interface's methods is same as overriding? [duplicate]

What is a covariant return type in Java? In object-oriented programming in general?
Covariant return, means that when one overrides a method, the return type of the overriding method is allowed to be a subtype of the overridden method's return type.
To clarify this with an example, a common case is Object.clone() - which is declared to return a type of Object. You could override this in your own class as follows:
public class MyFoo
{
...
// Note covariant return here, method does not just return Object
public MyFoo clone()
{
// Implementation
}
}
The benefit here is that any method which holds an explicit reference to a MyFoo object will be able to invoke clone() and know (without casting) that the return value is an instance of MyFoo. Without covariant return types, the overridden method in MyFoo would have to be declared to return Object - and so calling code would have to explicitly downcast the result of the method call (even thought both sides "know" it can only ever be an instance of MyFoo).
Note that there's nothing special about clone() and that any overridden method can have a covariant return - I used it as an example here as it's a standard method where this is often useful.
Here is another simple example :
Animal class
public class Animal {
protected Food seekFood() {
return new Food();
}
}
Dog class
public class Dog extends Animal {
#Override
protected Food seekFood() {
return new DogFood();
}
}
It’s possible to modify the return type of the Dog’s seekFood() method to DogFood - a subclass of Food, as shown below:
#Override
protected DogFood seekFood() {
return new DogFood();
}
That’s perfectly a legal overriding, and the return type of Dog’s seekFood() method is known as covariant return type.
From the release of JDK 1.5, covariant types were introduced in Java. and I'll explain it to you with a simple case, : When we override a function the function is allowed to make changes to it's behaviour that's what you get to read in most of the books, but what they { authors } miss out on is that we can change the return type too.
check below link for clarification we can change the return type as long as it can be assigned to return type of Base version of the method.
So this feature of returning derived types is called COVARIANT...
Can overridden methods differ in return type?
covariant Return types simply means returning own Class reference or its child class reference.
class Parent {
//it contain data member and data method
}
class Child extends Parent {
//it contain data member and data method
//covariant return
public Parent methodName() {
return new Parent();
or
return Child();
}
}
To add to the above answers, overriding is possible among co-variant return types, with the constraint that the return type of the overriding method (subclass method) should be a subclass of the return type of the overridden method (superclass method). This is valid from Java 5 onwards.
Covariant return type specifies that the return type may vary in the same direction as the subclass
class One{
One get(){return this;}
}
class Two extends One{
Two get(){return this;}
void message(){
System.out.println("After Java5 welcome to covariant return type");
}
public static void main(String args[]){
new Two().get().message();
}
}
Before Java 5, it was not possible override any method
by changing the return type. But now, since Java5,
it is possible to override method by changing the return type
if subclass overrides any method
whose return type is Non-Primitive
but it changes its return type to subclass type.
It helps to avoid confusing type casts present in the class hierarchy
and thus making the code readable, usable and maintainable.
We get a liberty to have more specific return types when overriding
methods.
Help in preventing run-time ClassCastExceptions on returns
reference:
www.geeksforgeeks.org
The covariant return type in java, allows narrowing down return type
of the overridden method.
This feature will help to avoid down casting
on the client side. It allows programmer to program without the need
of type checking and down casting.
The covariant return type always
works only for non-primitive return types.
interface Interviewer {
default Object submitInterviewStatus() {
System.out.println("Interviewer:Accept");
return "Interviewer:Accept";
}
}
class Manager implements Interviewer {
#Override
public String submitInterviewStatus() {
System.out.println("Manager:Accept");
return "Manager:Accept";
}
}
class Project {
public static void main(String args[]) {
Interviewer interviewer = new Manager();
interviewer.submitInterviewStatus();
Manager mgr = new Manager();
mgr.submitInterviewStatus();
}
}
Other example is from Java,
UnaryOperator.java
#FunctionalInterface
public interface UnaryOperator<T> extends Function<T, T> {
/**
* Returns a unary operator that always returns its input argument.
*
* #param <T> the type of the input and output of the operator
* #return a unary operator that always returns its input argument
*/
static <T> UnaryOperator<T> identity() {
return t -> t;
}
}
Function.java
#FunctionalInterface
public interface Function<T, R> {
........
........
........
........
static <T> Function<T, T> identity() {
return t -> t;
}
}
Before Java5, it was not possible to override any method by changing the return type. But now, since Java5, it is possible to override method by changing the return type if subclass overrides any method whose return type is Non-Primitive but it changes its return type to subclass type.

Class using generics is calling wrong version of method (superclass rather than subclass)? [duplicate]

This question already has answers here:
Java generics type erasure: when and what happens?
(7 answers)
Closed 7 years ago.
I am trying to refactor some code, moving common code shared among a few caches to a base class. Here's a simplified concept of it:
public abstract class DBObject {
public void copyTo(DBObject other) {
other.setId(this.id);
}
}
public class Person extends DBObject {
public void copyTo(Person other) {
super.copyTo(other);
other.setName(this.name);
}
}
public class PersonCache extends Cache<Person> {
}
public abstract class Cache<T extends DBObject> {
Map<Long, T> idToCachedMap;
private Class<T> tObjectClass;
public void initialize() {
// does stuff to populate the idToCachedMap
}
public void updateCache(T cachedObjToUpdate) {
T cachedObj = idToCachedMap.get(cachedObjToUpdate.getId());
T oldCachedObj = tObjectClass.newInstance();;
cachedObj.copyTo(oldCachedObj); // PROBLEM HERE
// do other stuff...
}
}
The problem I'm running into is that when I call updateCache(Person) on a PersonCache, the copyTo methods that get invoked on the objects are that in DBObject, as opposed to the one in Person. As a result, only some of the data is actually copied (in this example case, the ID, but not the name).
It seems to me that since both cachedObj and oldCachedObjs are guaranteed to be Person objects if it's a PersonCache, the copyTo method that should be called is the one on the Person class.
I feel like there must be something about how generics work that I'm missing that is causing this behavior. I know if I override copyTo in the Person class to be a signature of copyTo(DBObject other) rather than copyTo(Person other) it then does call the copyTo on the person class - but that'd be a sloppy way to rewrite it and I think I'm missing something that might be cleaner.
You are not overwriting the copyTo method because you change the signature. And you invoke the method which exist in T.
Try this:
public abstract class DBObject<T extends DBObject> {
public void copyTo(T other) {
other.setId(this.id);
}
}
public class Person extends DBObject<Person> {
#Override
public void copyTo(Person other) {
super.copyTo(other);
other.setName(this.name);
}
}
You state It seems to me that since both cachedObj and oldCachedObjs are guaranteed to be Person objects if it's a PersonCache, the copyTo method that should be called is the one on the Person class.
Because of type erasure this is an incorrect assumption, at runtime all it knows is DBObject and obviously Object as well.
It knows nothing about T at runtime, it is erased and not available at runtime.
copyTo(T other) is not equivlent to copyTo(Person other) they are overloaded not overridden because of the type erasure. copyTo(T other) actually becomes copyTo(DBObject other) as your behavior is showing that it matches copyTo(DBObject other). This is the expected behavior.
Type Erasure behavior is very well documented here on SO and on the internet in general.

Name clash: The method add(Object) of type test2 has the same erasure as add(E) of type HashSet<E> but does not override it

import java.util.*;
class A extends HashSet<Integer> {
public boolean add(Object obj){ //compiler error
return true;
}
}
or
class Abc <T> {
public void add(T t){} //compiler error
public void add(Object i){} //compiler error (can't overload?)
}
Error:Name clash: The method add(Object) of type test2 has the same erasure as add(E) of type HashSet but does not override it
i do not know what is the concept behind above error can any one suggest where i can study this concept?
The concept at work here is called type erasure. HashSet defines a method add(T), and you define a method add(Object). At a glance one might think this is OK; that your method just overloads add. However, the erasure of T is Object and so the two have the same erased signature.
Now, that would be fine if your method properly overrode the method from HashSet. But to do so you should be using add(Integer) and not add(Object). You're not properly overriding the parent method, so instead it is reported as a conflict since a class cannot provide two methods with the same signature.
Your Abc example follows the same reasoning. The two methods you declared have the same erased signature so they clash.
Further Reading
Angelika Langer Generics FAQ
When does a method override its supertype's method?
Can a method of a non-generic subtype override a method of a generic supertype?
interface CollectionConverter<U> {
<T> List<T> toList(Collection<T> c);
void fooMethod(Class<?> c);
<E>Comparable<E> method3(E e);
Comparable<U> method4(U u);
}
class Overrider implements CollectionConverter<Integer> {
#Override
public List toList(Collection c) {
return null;
}
#Override
public void fooMethod(Class c) {
}
#Override
public Comparable method3(Object o) {
return null;
}
#Override
public Comparable method4(Integer u) {
return null;
}
}
This code works well.
In JLS: The notion of subsignature is designed to express a relationship between two methods whose signatures are not identical, but in which one may override the other. Specifically, it allows a method whose signature does not use generic types to override any generified version of that method. This is important so that library designers may freely generify methods independently of clients that define subclasses or subinterfaces of the library.
Did you try using Integer instead of Object obj i.e
public boolean add(Integer i)
{ //compiler error
return true;
}
The problem is that when you are extending Hashset , you are extending Integer Hashset and not the generic form. So, in the subclass your add method has to comply with the signature of the superclass method which is
public boolean add(Integer i) { }
If you want to extend from a totally generic Hashset implementation, try extending with
public class MyHashset extends Hashset<?> {
}
Then your add method should work with Object.

Rationale behind not providing / providing <T> before return type of a method/constructor in Java for Generic classes

What is the rationale behind not providing / providing before return type of a method/constructor in Java for Generic classes?
I cannot get my head around it.
Sometimes it wants it and sometimes not. Do we ever need it for constructors?
The rules seem to be random and cannot find a logical explanation for it.
<T> in the method definition means that the type is defined in the method signature, and used only within that method.
Put <T> before the return type of a method when you want a generic associated with that method, instead of the containing class.
class Foo {
// T is associated with the method
<T> T stuff(T x) ...
}
class Bar<T> {
// T is associated with the class
T stuff(T x) ...
}
class Baz<T> {
// S is associated with the method, T with the class
<S> T stuff(S x) ...
<S> S otherStuff(T x) ...
}
class WTF<T> {
// Legal, but redundant
<T> T stuff(T x) ...
}
Constructors are no different. It is possible to put generics in constructors, as in
class Weird {
// T is associated with the constructor only
<T> Weird(T arg) ...
}
This would be unusual, though. It is much more common to see constructors use class-level generics, as in
class Normal<T> {
// T is associated with the class, as usual
Normal(T arg) ...
}
Sometimes it wants it and sometimes
not.
In the following case, it is not necessary, because the generic type is declared/defined as part of the class definition:
public class Example<T> {
public T generateItem() { return null; };
}
In the following case, it is necessary, because the generic type is NOT declared/defined as part of the class definition (or elsewhere):
public class Example {
public <T> T generateItem() { return null; };
}
The rule is: is it declared somewhere in the context or not? That's it!

How to iterate over a wildcard generic?

How can I iterate over a wildcard generic? Basically I would like to inline the following method:
private <T extends Fact> void iterateFacts(FactManager<T> factManager) {
for (T fact : factManager) {
factManager.doSomething(fact);
}
}
If this code is in a separate method as shown, it works because the generic method context allows to define a wildcard type (here T) over which one can iterate. If one tries to inline this method, the method context is gone and one cannot iterate over a wildcard type anymore. Even doing this automatically in Eclipse fails with the following (uncompilable) code:
...
for (FactManager<?> factManager : factManagers) {
...
for ( fact : factManager) {
factManager.doSomething(fact);
}
...
}
...
My question is simply: Is there a way to put some wildcard type one can iterate over, or is this a limitation of generics (meaning it is impossible to do so)?
No. In situation like this, the workaround is to create a helper method.
The JLS has this example http://java.sun.com/docs/books/jls/third_edition/html/conversions.html#5.1.10
public static void reverse(List<?> list) { rev(list);}
private static <T> void rev(List<T> list) { ... }
The issue is, we have a List<?> object. We know it must be a List<X> of some X, and we'd like to write code using X. Internally compiler does convert the wildcard to a type variable X, but Java language does not offer programmers a direct way to access it. But if there's a method accepting List<T>, we can pass the object to the method. Compiler infers that T=X and the call is good.
If there's no type erasure, X can be known at runtime, then Java would definitely give us a way to access X. However as of today since X isn't available at runtime, there's not much point. A purely synthetic way could be provided, which is unlikely to be simpler than the helper method workaround.
Type parameters can only defined on
types (i.e. classes/interfaces),
methods, and
constructors.
You would need a type parameter for a local block, which is not possible.
Yeah, I missed something like this sometimes, too.
But there is not really a problem with having the method non-inlined here - if it presents a performance bottleneck where inlining would help, Hotspot will inline it again (not caring about the type).
Additionally, having a separate method allows giving it a descriptive name.
Just an idea, if you need this often:
interface DoWithFM {
void <T> run(FactManager<T> t);
}
...
for (FactManager<?> factManager : factManagers) {
...
new DoWithFM() { public <T> run(FactManager<T> factManager) {
for (T fact : factManager) {
factManager.doSomething(fact);
}
}.run(factManager);
...
}
...
You can always fall back to Object
for (FactManager<?> factManager : factManagers) {
...
for ( Object fact : factManager) {
factManager.doSomething(fact);
}
...
}
This, of course, is subject to what is the actual declaration of doSomething.
If doSomething is declared as this void doSomething( T fact ), then your recourse here would be to use a raw type and swallow unchecked warnings. If you can guarantee that FactManager can only have homogeneous Facts inserted, then that may be an OK solution.
for (FactManager factManager : factManagers) { // unchecked warning on this line
...
for ( Object fact : factManager) {
factManager.doSomething(fact);
}
...
}
Well, I can think of a way to do it using inner classes, because the inner class shares the type parameter with its enclosing type. Also, even using wildcards you could still process your collections thanks to wildcard capture conversion.
Let me create an example. This code compiles and runs fine. But I cannot be certain if the use of inner classes would be an issue for you.
//as you can see type parameter belongs to the enclosing class
public class FactManager<T> implements Iterable<FactManager<T>.Fact> {
private Collection<Fact> items = new ArrayList<Fact>();
public void doSomething(Fact fact) {
System.out.println(fact.getValue());
}
public void addFact(T value) {
this.items.add(new Fact(value));
}
#Override
public Iterator<Fact> iterator() {
return items.iterator();
}
public class Fact {
//inner class share its enclosing class type parameter
private T value;
public Fact(T value) {
this.value = value;
}
public T getValue() {
return this.value;
}
public void setValue(T value) {
this.value = value;
}
}
public static void main(String[] args) {
List<FactManager<String>> factManagers = new ArrayList<FactManager<String>>();
factManagers.add(new FactManager<String>());
factManagers.get(0).addFact("Obi-wan");
factManagers.get(0).addFact("Skywalker");
for(FactManager<? extends CharSequence> factManager : factManagers){
//process thanks to wildcard capture conversion
procesFactManager(factManager);
}
}
//Wildcard capture conversion can be used to process wildcard-based collections
public static <T> void procesFactManager(FactManager<T> factManager){
for(FactManager<T>.Fact fact : factManager){
factManager.doSomething(fact);
}
}
}
This is more precisely matched to the method you defined (that is, if you can call iterateFacts() with the FactManagers in factManagers, you know that the FactManager contain items that are some subclass of Fact).
for (FactManager<? extends Fact> factManager : factManagers) {
for (Fact fact : factManager) {
factManager.doSomething(fact);
}
}
I would tend to think, however, that you would declare FactManager to be generic for subtypes of Fact (just given the name of the class), e.g.
class FactManager<T extends Fact> implements Iterable<T> {
...
}
The Eclipse refactoring fails because it cannot infer the type of an object contained by FactManager<?>.

Categories

Resources