There's the following class:
public class LivingBeing { … }
Then there's
public class Human extends LivingBeing { … }
Now there's also this wrapper:
public class LivingBeingWrapper<T extends LivingBeing> { … }
And to complete the picture there's also the method
public boolean validate(LivingBeingWrapper<LivingBeing> livingBeingWrapper)
Now when writing the following code
LivingBeingWrapper<Human> wrapper = createHumanWrapper();
validate(wrapper);
I get the following compile error:
The method validate(LivingBeingWrappe<LivingBeing>
livingBeingWrapper) in the type MyType is not applicable for the
arguments (LivingBeingWrapper<Human>).
But why? Human extends LivingBeing.
A banana is-a fruit. But a list of bananas is not a list of fruit. Otherwise you could take a list of bananas and add an apple (given that an apple is-a fruit).
That sounds rather gnomic, but it's key to what's happening above. You need to specify your wrapper such that it takes types extending LivingBeing.
For further info, see this article, and in particular the "Generics are not Covariant" section.
Your validate method declares that it must be called with a LivingBeingWrapper parameterised with LivingBeing. However, you're passing in a LivingBeingWrapper parameterised with Human. Try changing your method declaration from this:
public boolean validate(LivingBeingWrapper<LivingBeing> livingBeingWrapper)
to this:
public boolean validate(LivingBeingWrapper<? extends LivingBeing> livingBeingWrapper)
Change your below method: -
public boolean validate(LivingBeingWrapper<LivingBeing> livingBeingWrapper)
to
public boolean validate(LivingBeingWrapper<T> livingBeingWrapper)
Since this method is inside your class which is like: -
public class LivingBeingWrapper<T extends LivingBeing>
So, you can use the type T in your method parameter, and T will be replaced by Horse.. For your particular reference..
And if your method takes some different type than the one in class definition (T), then you can use this declaration: -
public boolean validate(LivingBeingWrapper<? extends LivingBeing>
livingBeingWrapper)
Now, if you have to call validate(LivingBeingWrapper<Human>), your method has to be defined to take generic type parameter type Human, which extends LivingBeing, which is what your T type is (As it also extends LivingBeing in your class definition)
Related
I'm having some trouble creating a method to add objects which extend an abstract class(Ant_Abstract) to an ArrayList of type objects extending Ant_Abstract. So far I have two kinds of ants, Scouts and Workers(both extending Ant_Abstract), and I want to use an ArrayList to contain both kinds of Ants in the file Anthill.
When I try to compile:
import java.util.*;
class Anthill{
public ArrayList<? extends Ant_Abstract> occupants=new ArrayList(15);
Anthill(){
}
public<t extends Ant_Abstract> void enter(Class<t> ant){
occupants.add(ant);
}
}
it gives me the error: "The method add(capture#1-of ? extends Ant_Abstract) in the type ArrayList is not applicable for the arguments (Class)
Please help.
In order to use the 'T' as a generic type, you need to indicate this at the class declaration. Then within properties and methods on your class Anthill, you can then reference it throughout:
public class Anthill<T extends Ant_Abstract>{
public ArrayList<T> occupants = new ArrayList<>(15);
public void enter(T ant){
occupants.add(ant);
}
}
You need to change the declaration of your ArrayList to this:
public ArrayList<T> occupants = new ArrayList<>(15);
Change Class<T> to just T
You are not trying to add the Class (which is an object type in itself).
Your intention is to add an instance of Abstract_Ant implementation, not the Class itself.
Take a moment to read over the Java Generics Lesson
I am trying to write an Interface with a setter method that takes in a TreeMap as an argument.
That TreeMap can have objects of classes descended from an AbstractNResource class. The class definition of AbstractNResource is written with generics to let it take in two arguments from two other class hierarchies.
I have had trouble writing a setter method in the Interface that the compiler will accept.
Here is the Interface (with the compile error), I call it IDataLoader:
public interface IDataLoader {
public abstract void setFrameDataMap(
// Compile Error: Syntax error on token "extends", , expected
TreeMap<Long, AbstractNResource<AbstractNRequestableResourceData, INDescriptor>.AbstractFrameData> frameDataMap);
}
This is the parent class of the kind of objects that TreeMap can have:
public abstract class AbstractNResource<T extends AbstractNRequestableResourceData, D extends INDescriptor>
extends
AbstractVizResource<AbstractNRequestableResourceData, IDescriptor>
implements INResource {
public abstract class AbstractFrameData {}
protected TreeMap<Long, AbstractFrameData> frameDataMap;
}
Next step down, I have an AbstractDataLoader class that partially implements the Interface IDataLoader:
public abstract class AbstractDataLoader<T extends AbstractNatlCntrsRequestableResourceData, D extends INatlCntrsDescriptor>
implements
IDataLoader<AbstractNatlCntrsRequestableResourceData, INatlCntrsDescriptor> {}
One more step down I have a class WaveSatDataLoader that extends that abstract class. This is where the compile error is happening:
public class WaveSatDataLoader extends
AbstractDataLoader<WaveSatResourceData, NCMapDescriptor> {
#Override
public void setFrameDataMap(TreeMap<Long,AbstractNatlCntrsResource<WaveSatResourceData, NCMapDescriptor>.AbstractFrameData> frameDataMap) {
System.out.println("To do");
}
}
The compilation error is:
The method
setFrameDataMap(TreeMap.AbstractFrameData>)
of type WaveSatDataLoader must override or implement a supertype
method
I do not have setFrameDataMap() implmentation in the AbstractDataLoading class, but putting one there hasn't helped.
I am stumped as to why the compiler is complaining.
Any help would be greatly appreciated
Thanks
The compiler is complaining because you are trying to implement an interface method with a method that has a different signature.
In IDataLoader, the second type parameter of the argument to setFrameDataMap is
AbstractNResource<AbstractNRequestableResourceData, INDescriptor>.AbstractFrameData
whereas in WaveSatDataLoader it is
AbstractNatlCntrsResource<WaveSatResourceData, NCMapDescriptor>.AbstractFrameData
I am also confused about whether IDataLoader is supposed to be generic or not. You have written
implements IDataLoader<AbstractNatlCntrsRequestableResourceData, INatlCntrsDescriptor>
yet in the first section of code IDataLoader does not have type parameters.
I have a class defined as follows:
public class MultiFacilitySearchDataProvider <T extends Facility & MilepostValue & Copyable<T>>
The class has this method:
protected T getFacility(final FacilityInstance instance) {
final MultiFacilitySearchDAO dao = new MultiFacilitySearchDAO(instance);
ENM.execute(dao);
return dao.getResultModel(); // Compile error here
}
The DAO method called above looks like this:
public MultiFacilitySearchModel getResultModel() {
return resultModel;
}
And MultiFacilitySearchModel is defined as follows:
public class MultiFacilitySearchModel implements Copyable<MultiFacilitySearchModel>, Facility,
Serializable, MilepostValue, MenuDisplayValues
The problem is that the line return dao.getResultModel() generates a compile error "Type mismatch: cannot convert from MultiFacilitySearchModel to T". I don't understand why I'm getting this error, since MultiFacilitySearchModel implements the three interfaces specified for the generic type T. What am I missing?
You can specify any class that meets the prequesites as type parameter. Therefore T need not be a supertype of MultiFacilitySearchModel. That means however, that
return dao.getResultModel()
may not return an object of a type that extends T. Java doesn't allow you to do this.
The method MultiFacilitySearchDAO.getResultModel() has no generic type and will allways return MultiFacilitySearchModel instead of T.
Although MultiFacilitySearchModel can be used as T in some class that extends MultiFacilitySearchDataProvider.
In your class MultiFacilitySearchDAO you have the method
public MultiFacilitySearchModel getResultModel()
which is obviously returning an instance of MultiFacilitySearchModel. There is nothing generic here. The return type is fix.
Your class MultiFacilitySearchDataProvider on the other hand declares the method
protected T getFacility(final FacilityInstance instance)
The type variable T is constrained to implement the three interfaces Facility & MilepostValue & Copyable. This is much less then being a MultiFacilitySearchModel.
I could easily create a class
public MyFacilitySearchModel implements Copyable<MyFacilitySearchModel>, Facility, MilepostValue
and then use a
MultiFacilitySearchDataProvider<MyFacilitySearchModel>
The result type of getResultModel() would still be MultiFacilitySearchModel which can't be casted to MyFacilitySearchModel.
Maybe you DAO class should also be generic. Or declare the method as
protected MultiFacilitySearchModel getFacility(final FacilityInstance instance)
Situation: May Field be a property of a Model which I want to observe (something similar to PropertyChangeListener). Field is defined as Enum. I want to call
someModel.getField(Field f).addObserver(FieldObserver<Something> observer)
the Observer's type ("Something") depends on the Field and I want to force typesafety. For example Field.FIRST_NAME would take a FieldObserver< String>.
First (working) approach:
I could already reach it by defining Field as generic class with public static final fields, instead of enum. So I have a Field, which I can use to force type safety like this:
public <E> Observable<E> getField(Field<? extends E> f)
with method in Observable<E>: void addObserver(FieldObserver<? super E> observer)
Now the following line causes a compile time error, which is what I want:
someModel.getField(Field.some_STRING_field).addObserver(INTEGERObserver)
Second (not working yet) approach:
I am trying now to implement the same behaviour with the enum class. One of my approach is:
public enum Field{
FIRST_NAME("firstName") {
#Override
public Observable<String> getObservable() {
return new Observable<String>();
}
},...
//every Field must implement:
public abstract FieldObservable.Observable<?> getObservable();`
}
//getField(Field f) of the Model-class:
public Observable<?> getField(Field f){
return f.getObservable();
}
Trying to add a FieldObserver of any type (also the correct one) gives me a compile time error like this:
addObserver (enumer.FieldObserver< capture< ?>>) in Observable cannot be applied
to (enumer.FieldObserver< java.lang.String>)
Can someone tell me how to make the enum-approach work? Also if someone has a better solution or concerns about the approaches, I will be thankful to hear it.
Unfortunately enums cannot be generic. This is because enum is an ordinary class that extends Enum, i.e. its definition looks like the following:
class MyEnum<E extends Enum<E>> extends Enum<E extends Enum> {}
So application programmer cannot add more generic parameter to this class.
I can suggest you the following work arounds:
Make your method generic, ie. define it in enum level as public <T> getField(Class<T> type). Override this method for each enum member. Then you can supply the type at any call of the method.
Do not use enum for this task. Create ordinary class with public static members and private constructor. if you want enum-like behavior implement your own static values() and valueOf().
I've got problem in my code in Java. I have four(important) Classes:
public class RDOutput extends OutputType
public class RDAnalysis extends AnalysisProperties
Now I'm trying to make a method in Analysis properties:
public abstract void display(ArrayList<? extends OutputType> results);
The main problem list, the objects in the ArrayList will be different subtypes of OutputType. In my class RDAnalysis I try to make specific overriding:
public void display(ArrayList<RDOutput> results) {
but eclipse says: Name clash: The method display(ArrayList) of type RDAnalysis has the same erasure as display(ArrayList? extends OutputType) of type AnalysisProperties but does not override it
I'm not familiar with Java tricks, I tried searching in documentation and I didn't find any solution to this problem.
My question is: Is that trick that I'm doing (Basic type in abstract and Extended in final function) possible in Java (if yes, how can I do that?) or do I have to make some enum to solve this?
I suggest you to introduce generic parameter to your class and use it to parametrize your method:
public abstract class A<T extends OutputType> {
public abstract void display(ArrayList<T> results);
}
public class B extends A<RDOutput> {
public void display(ArrayList<RDOutput> results) {}
}
It's because your display doesn't cover every case of the abstract method. Maybe try something like this :
public class RDOutput extends OutputType {}
public class OutputType {}
public abstract class AnalysisProperties<T extends OutputType> {
public abstract void display(ArrayList<T> results);
}
public class RDAnalysis extends AnalysisProperties<RDOutput> {
#Override
public void display(final ArrayList<RDOutput> results) {
}
}
The problem is that you try to override a method while restricting possible parameters.
=> ArrayList<? extends OutputType> accepts more possible elements than ArrayList<RDOutput> since RDOutput extends OutputType.
You break the rule that says: the concerned subclass method has to encompass at least elements of superclass one and NEVER restrict them.
So compiler avoid to valid this override.
By the way, avoid to type your reference with concrete values like ArrayList.
What about a LinkedList passed as arguments? ... prefer a more generic relevant type like List.
Problem here is that, after type erasure comes into play, the signature of the two methods are undistinguishable: they have the same return type and they can both accept a ArrayList<RDOutput> but the first one (the generic one) can also accept any ArrayList<T extends OutputType>.
This mean that, although the JVM won't be able to choose which one to call at runtime if you pass an ArrayList<RDOutput>, at the same time your display method does not override the abstract one because your method only work for lists of RDOutput, so if you pass a List<T extends OutputType> with T != RDOutput your specific implementation doesn't accept it.
You should consider using a type parameter on the whole class as suggested in other answers, or accept the fact that you won't be able to use any RDOutput specific methods in your display method without a cast.
if a method is expecting ArrayList<? extends OutputType>
ArrayList<RDOutput> cannot be passed to it, as parent type allows any child class of OutputType in arraylist.
consider a code like this
AnalysisProperties properties = new RDAnalysis();
properties.display(arraylist consisting of any child class of OutputType); //this line will cause runtime problems