This question already has answers here:
Java erasure with generic overloading (not overriding)
(3 answers)
Closed 3 years ago.
I wanted to know if it is possible to overload a method by differing in the generic parameter. Example:
class Foo <T> { ... }
class Bar {
public void do (Foo<Integer> obj) {}
public void do (Foo<Double> obj) {}
}
I know this will not be compiled. But for example in kotlin you have the annotation JvmName where you can annotate these methods to get compiled the code. Do Java has this possibility, too?
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.
Related
This question already has answers here:
How to implement the same interface multiple times, but with different generics? [duplicate]
(3 answers)
Closed 6 months ago.
I have seen many question around this, but none seens to be exactly what I am facing and I still don't understand what's wrong.
I have a class that implements two interfaces and these two interfaces extends from another interface with generics. Whoever it seens like I can't implement these two interfaces in the same class, as I have an error saying 'cannot be inherited with different type arguments'.
Interface with Generics:
public interface Observer<O> {
void onEvent(O data);
}
Interface A:
public interface EventAObserver extends Observer<String> {
}
Interface B:
public interface EventBObserver extends Observer<Integer> {
}
Class that needs to listen both events and gets the error:
public class Listener implements EventAObserver, EventBObserver {
#Override
public void onEvent(Integer data) {
}
#Override
public void onEvent(String data) {
}
}
All this trouble because somewhere else in my code I have a list of observers and I want to broadcast events just like:
for (Observer observer : observers)
observer.onEvent(data);
Is it possible to solve this inheritance problem? Or should I try something entirely diffent?
Thanks!
You should try something different because of type erasure for generics during runtime. This means during runtime the specified types for generics all are replaced by Object. And therefore the onEvent methods in Listener look the same during runtime.
This question already has answers here:
What is a raw type and why shouldn't we use it?
(16 answers)
Closed 4 years ago.
Here is a pathological generics example in Java. What is going on here?
public abstract class Foo<X> {
private List<String> stuff;
public List<String> getStuff() {
return stuff;
}
public void setStuff(List<String> stuff) {
this.stuff = stuff;
}
}
Then I created a subclass, but not I did not specify the type bound, which should make it object.
public class Bar extends Foo {
public Bar() {
setStuff(new ArrayList<>());
}
public void whatIsGoingOnHere() {
for(String thing : getStuff())
System.out.println("Why is this a compiler error???");
}
}
Why is this a compiler error?
You call setStuff(new ArrayList<>());. Here ArrayList<> isn't not bound, it is inferred by the compiler if it can. And it can as setStuff is setStuff(List<String>). So the compiler knows it is a ArrayList<String> and uses (infers) that.
Your loop iterates over an List<String> as returned by the getStuff() method, so defining the thing as a String (or any super class or interface) will be okay for the compiler.
The base class does have an X type, but it doesn't matter as your stuff list is declared with a type. Only if you would define stuff as an List<X> it would matter what the subclass defined for X.
This question already has answers here:
Whats the use of saying <? extends SomeObject> instead of <SomeObject>
(5 answers)
Closed 6 years ago.
I have seen some code as follows
public interface IBean {
}
and its usage at some places as
public void persist(List<? extends IBean> beansList) {
}
However same can achieved with following code
public void persist(List<IBean> beansList) {
}
So what is the difference between both methods, both are excepting objects that must inherit IBean interface?
Here are the bean classes
public class Category implement IBean {
//related fields
}
public class Product implement IBean {
//related fields
}
You can pass a List<Category> to public void persist(List<? extends IBean> beansList), but you cannot pass a List<Category> to public void persist(List<IBean> beansList).
On the other hand, you can pass a List<IBean> to both methods.
The reason is that generics are invariant. This means for example that you can't use a List<Integer> where a List<Number> is expected.
But when turning to wildcards, you can circumvent that restriction. Therefore, when you really have a List<Product> you will not be able to pass that into a method that expects List<IBean> - you would have to somehow convert the list first. To be precise: you would do a "hard" cast; as there is no point in "converting" generic lists, as type erasure kicks in at runtime anyway!
By using the wildcard on the method definition, you can allow for passing Lists that use "real" sub classes of the extended type; without the need of ugly casts.
This question already has answers here:
Create instance of generic type in Java?
(29 answers)
Closed 7 years ago.
I have this code:
ObjProcessor processor = getProcessor();
MyClass myObj = getObjToProcess();// MyClass extends PersistentObj
and classes:
public class ObjProcessor {
public <OP extends PersistentObj) void process(Class<OP> objClazz, OP object, Modifier<OP> modifier) {
...
}
}
public interface Modifier<T> {
void modify(T obj);
}
I am stuck. How do I create an instance of the Modifier to be able to invoke:
processor.process(myObj.getClass(), myObj, ???);
After Ron C's comment, I created this Modifier:
Modifier<MyClass> mod = new Modifier<MyClass>() {
#Override
public void modify(MyClass obj) {
// empty
}
};
proc.process(myObj.getClass(), myObj, mod); // compilation error!
Eclipse gave this error:
The method process(Class<OP>, OP, Modifier<OP>) in the type ObjProcessor is not applicable for the arguments (Class< capture#1-of ? extends MyClass>, MyClass, Modifier<MyClass>)
You can create an anonymous inner class as an instance of the Modifier interface:
processor.process(myObj.getClass(), myObj, new Modifier<MyClass>() {
#Override
public void modify(MyClass obj) {
//Add implementation here
}
});
If you're using java 8, you can also use lambada expressions. Because your interface is considered as a Functional Interface (interface with only one method), you can use lambada expression instead of creating anonymous class:
processor.process(myObj.getClass(), myObj, obj -> {
//Add implementation here
});
For the second problem, the solution is to change the declaration of process to:
public <OP extends ObjProcessor> void process(Class<? extends OP> objClazz, OP object, Modifier<OP> modifier) {
}
I've replaced Class<OP> with Class<? extends OP>. The older decleration only works with: MyClass.class, but not with: instanceOfMyClass.getClass().
The reason for this is that the Class<OP> type argument can't accept Class<ClassExtendsOP> as an argument, it's only allow one class.
If your'e using MyClass as OP,
when you're using MyClass.class, you're always getting Class<MyClass> object. But when you're using instanceOfMyClass.getClass(), you can get Class<ClassExtendsMyClass>, which not match the argument type.
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.