Java generics cast and usage - java

I have kind of problem with generics inheritance. Below is the dependency tree:
public class Attributes {
}
public abstract class Feature<T extends Attributes> {
private T attributes;
public T getAttributes() {
return attributes;
}
public void setAttributes(T attributes) {
this.attributes = attributes;
}
}
public abstract class AbstractFeatureRepository<T extends Feature<? extends Attributes>> {
public abstract String getType();
public abstract boolean create(T feature);
}
And I have implementations of these feature repositories, like this:
public class NodeAttributes extends Attributes {
private String startPoint;
public String getStartPoint() {
return startPoint;
}
public void setStartPoint(String startPoint) {
this.startPoint = startPoint;
}
}
public class Node extends Feature<NodeAttributes> {
}
public class NodeRepository extends AbstractFeatureRepository<Node> {
public String getType() {
return "Node";
}
public boolean create(Node node) {
return true;
}
}
public class LinkAttributes extends Attributes {
private String uri;
public String getUri() {
return uri;
}
public void setUri(String uri) {
this.uri = uri;
}
}
public class Link extends Feature<LinkAttributes> {
}
public class LinkRepository extends AbstractFeatureRepository<Link> {
public String getType() {
return "Link";
}
public boolean create(Link link) {
return true;
}
}
I'm injecting these repositories with Spring to Controller via constructor (but in this example I'm manually creating in constructor for the sake of simplicity):
public class Controller {
private final Map<String, AbstractFeatureRepository<? extends Feature>> featureRepositories;
public Controller() {
this.featureRepositories = Arrays.asList(new NodeRepository(), new LinkRepository()).stream()
.collect(Collectors.toMap(AbstractFeatureRepository::getType, Function.identity()));
}
public Node createNode() {
Node newNode = new Node();
newNode.getAttributes().setStartPoint("Berlin");
createFeature("Node", newNode);
return newNode;
}
public Link createLink() {
Link newLink = new Link();
newLink.getAttributes().setUri("/home/local");
createFeature("Link", newLink);
return newLink;
}
private void createFeature(String type, Feature<? extends Attributes> feature) {
featureRepositories.get(type).create(feature);
}
}
All is good untill I want to call "create" method in generic "createFeature" where I'm getting compilation error that
The method create(capture#5-of ? extends Feature) in the type AbstractFeatureRepository<capture#5-of ? extends Feature> is not applicable for the arguments (Feature<capture#6-of ? extends Attributes>)
What I'm doing wrong? Is this because potentially I can pass some other kind of "Feature" than particular "Repository" can work with? How then should I change my map Repositories in Controller so compiler doesn't complain? I though I should operate exact classes as a key for map but have no idea how to make all of this working. Any suggestions?
Thank you.
Update 1. I changed Map to
private final Map<Class<?>, AbstractFeatureRepository<? extends Feature>> featureRepositories;
Changed AbstractFeatureRepository so it now looks this way:
public abstract class AbstractFeatureRepository<T extends Feature> {
public abstract Class<T> getClazz();
public abstract boolean create(T feature);
}
And changed the methods in controller:
public Link createLink() {
Link newLink = new Link();
createFeature(Link.class, newLink);
return newLink;
}
private <T extends Feature> void createFeature(Class<T> class1, T feature) {
AbstractFeatureRepository<? extends Feature> abstractFeatureRepository = featureRepositories.get(feature.getClass());
abstractFeatureRepository.create(abstractFeatureRepository.getClazz().cast(feature));
}
It still doesn't allow me to do that.

This code:
featureRepositories.get(type)
returns an object whose type is the V in Map<K, V>, as per the docs of java.util.Map. In your code, that means that expression is of type AbstractFeatureRepository<? extends Feature<? extends Attributes>>.
Let's simplify matters a tad, and assume we have List<? extends Number> instead.
This is valid java code:
List<? extends Number> list = new ArrayList<Integer>();
that's sort of the point of ? extends, really. This does not compile:
List<Number> list = new ArrayList<Integer>();
Now, imagine you called, on your List<? extends Number>:
List<? extends Number> list = new ArrayList<Integer>();
Number n = Double.valueOf(5.0);
list.add(n);
uhoh. There is a non-integer in my list of integers. Whoops.
That's why you can't call add() here, at all. You cannot call add on a List<? extends whatever>, at all. Any method that takes as argument a T where your type is Foo<? extends T> cannot be invoked*.
Let's go back to your code now:
You have a AbstractFeatureRepository<? extends Feature<? extends Attributes>> - therefore any method that AbstractFeatureRepository has that takes a T cannot be invoked from this. at all. And create is such a method.
The solution is a bit tricky. You can use a type-safe container, if you somehow have a reference to a class representing T (careful; things can be a T that cannot be a Class: List<Integer> is a T, but only List.class exists, you can't write List<Integer>.class! - You can use that:
public <T extends Attribute> void createFeature(Class<T> typeDesired, Feature<T> feature) {
featureRepositories.get(type).create(typeDesired.cast(feature));
}
is one way.
In general your method signature is problematic: There is just no guarantee that your stringly-typed String type implies that the kind of feature desired Feature<? extends Attribute> is handled by the repository matching that type.
A second option is to have each AbstractFeatureRepository responsible to deal with type mismatches. In that case, you can update the interface to read create(Object feature) throws TypeMismatchException instead. Or, have it return a type (public Class<T> getType()) and you can go back to the cast construct.
*) Well, you can invoke it, if you pass literally null, as null is all data types. But that clearly isn't what you intend to do here, thus, not relevant.

If you've only got 2 things in a Map (or N things, where N is a small number, you mention 4 in a comment), and you use fixed keys to indicate a particular type, using a Map is making it harder than necessary.
Maps are necessarily homogeneously typed, that is, all the keys have a common supertype, and all the values have a common supertype. The issue that you're facing is that you want a key's type to relate to the value's type: this can be done with a type-safe heterogeneous container (essentially: a map which you construct such that you can impose the constraints on the relationships between the types). But even this is overly-complex for the problem described.
Use two (N) fields instead:
public class Controller {
private final NodeRepository nodeRepository = new NodeRepository();
private final LinkRepository linkRepository = new LinkRepository();
This is still sort-of a map: the key is the field, the value is the field value.
But the advantage of this is that you've retained the concrete types of the repositories, so you can pass these to the method:
private <A extends Attributes> void createFeature(AbstractFeatureRepository<A> repo, Feature<A> feature) {
repo.create(feature);
}
e.g.
public Node createNode() {
Node newNode = new Node();
newNode.getAttributes().setStartPoint("Berlin");
// Or, easier, nodeRepository.create(newNode);
createFeature(nodeRepository, newNode);
return newNode;
}
public Link createLink() {
Link newLink = new Link();
newLink.getAttributes().setUri("/home/local");
// Or, easier, linkRepository.create(newNode);
createFeature(linkRepository, newLink);
return newLink;
}

To arrive at a working solution that is as close to your original code as I could get it, I made three or four relatively minor refactors.
The most significant change though was in Controller.createFeature()…
private <T extends Feature<?>> void createFeature(Class<T> class1, T feature) {
AbstractFeatureRepository<T> abstractFeatureRepository = (AbstractFeatureRepository<T>)featureRepositories.get(feature.getClass());
abstractFeatureRepository.create(feature);
}
The cast there is the simplest, most type safe solution in my opinion. The reason I'm convinced the cast is type safe is because of the compilation error you'd get if the cast weren't there:
Controller.java:31: error: incompatible types: AbstractFeatureRepository<CAP#1> cannot be converted to AbstractFeatureRepository<T>
AbstractFeatureRepository<T> abstractFeatureRepository = featureRepositories.get(feature.getClass());
where T is a type-variable:
T extends Feature<?> declared in method <T>createFeature(Class<T>,T)
where CAP#1 is a fresh type-variable:
CAP#1 extends Feature<?> from capture of ? extends Feature<?>
1 error
If you read the bottom part of the error message carefully, you'll see that the only difference between T extends Feature<?> and CAP#1 extends Feature<?> is the names of the two type variables. They both have the same upper bounds (extends Feature<?>). That tells me it's reasonable to infer that a cast would be type safe.
So, I annotated that method with SuppressWarnings("unchecked").
To confirm that the solution is usable, I fleshed out Node and Link classes with toString(). Calling Controller.createNode() and Controller.createLink() in the solution demo gets you…
Node: [NodeAttributes - Start Point: 'Berlin']
Link: [LinkAttributes - URI: 'urn::foo::bar']
I have to admit that what problem you're trying to solve isn't crystal clear to me. So I've made some assumptions based on only my general Java knowledge. Please let me know if the solution meets your requirements?

Here is the approach I used:
public class Controller {
private final Map<Class<?>, AbstractFeatureRepository<? extends Feature>> featureRepositories;
public Controller3(List<AbstractFeatureRepository<? extends Feature>> featureRepositories) {
this.featureRepositories = featureRepositories.stream()
.collect(Collectors.toMap(AbstractFeatureRepository::getClazz, Function.identity()));
}
public Node createNode() {
Node newNode = new Node();
createFeature(Node.class, newNode);
return newNode;
}
public Link createLink() {
Link newLink = new Link();
createFeature(Link.class, newLink);
return newLink;
}
private <T extends Feature> void createFeature(Class<T> clazz, T feature) {
AbstractFeatureRepository<T> repository = getRepository(clazz);
repository.create(feature);
}
#SuppressWarnings("unchecked")
private <T extends Feature, V extends AbstractFeatureRepository<T>> V getRepository(Class<T> clazz) {
return (V) featureRepositories.get(clazz);
}
public static void main(String[] args) {
Controller3 controller = new Controller3();
controller.createLink();
}
}
It doesn't satisfy completely no-cast approach(especially no #SuppressWarnings) but it is the least evil for me, since cast is done only in one method in controller, all the rest methods work no cast and no #SuppressWarnings.

Try
private static <T extends AbstractFeatureRepository> void createFeature(Class<T> clazz, Feature<? extends Attributes> feature) {
((T) featureRepositories.get(clazz)).create(feature);
}
You should modify the featureRepositories accordingly
private static final Map<Class<?>, AbstractFeatureRepository<? extends Feature>> featureRepositories
But I don't recommend using generics like this.

Related

Using self-referential generic types in Java

Consider the following Java method:
<T extends List<T>> List<T> getMyList() {
return Collections.emptyList();
}
I can assign its output to a variable with a raw type, like so:
List x = getMyList();
List<List> y = getMyList();
But, I can't think of any way to assign its output to a fully parameterized type. In particular, I can't think of a non-raw, concrete type T that would satisfy List<T> z = getMyList();
Can we create such a T ?
If not, why not?
For context, I created this question while trying to understand how Enums are implemented in Java.
Here's an example of a concrete type that both works and starts to hint at a possible use-case (registration of some sort). The type consists acts like both an instance of some type, and as a container for all instances of that type.
public class WeirdEnum extends AbstractList<WeirdEnum> {
private static List<WeirdEnum> underlyingList = new ArrayList<>();
#Override
public WeirdEnum get(int index) { return underlyingList.get(index); }
#Override
public int size() { return underlyingList.size(); }
static <T extends List<T>> List<T> getAList() {
return Collections.emptyList();
}
public WeirdEnum() {
underlyingList.add(this); // Sufficient for our example but not a good idea due to concurrency concerns.
}
static List<WeirdEnum> foo = WeirdEnum.getAList();
}
Not sure if I fully understand your question, but here's an example:
class Example<T> implements List<Example<T>> {
...
}
...
List<Example<String>> list = getMyList();
Every enum in Java extends from the base-enum-class Enum<T extends Enum<T>>, where T is the actual type of the implementing enum.
When writing SomeClass<T extends SomeClass<T>> you can enforce that the type-parameter is always the implementing class itself.
Let's say you have this interface:
public interface MyInterface<T extends MyInterface<T>> {
T getSelf();
}
And this implementing class:
public class MyClass implements MyInterface<MyClass> {
public MyClass getSelf() {
return this;
}
}
In MyClass it is not possible to use any other type-parameter than MyClass itself.

Number.valueOf() implementation for Java generics

I have been using a Collection of number values and have implemented some related functionality (mapping x->y values, etc). So far I have made use of generics to allow any subclass of Number in the Collection.
In this particular class I keep running into the problem that there is no easy way to cast to the generic. The usual methods like Double.valueOf() cannot be invoked because Number does not provide such a method.
Is there a good way around this?
I have found this post and thought that would solve it, but I cannot pass the Class.class parameter to it.
public class myList<T extends Number> extends Collection<T> {
#Override
public boolean add(T value){
// this is no problem
}
// What I want to do:
public boolean add(double value){
return this.add(T.valueOf(value));
}
// Using the example I found:
public void add(double value){
return this.add( (T) Ideone.parse(value, T.class) ); // no such option
}
}
Thanks in advance.
There's no way for the myList class to be able to convert double to T, because T is unknown.
One way you can solve this is to let the caller provide a function that converts from double to T. Here's an example:
public class myList<T extends Number> implements Collection<T> {
private DoubleFunction<T> fromDoubleFunction;
public myList(DoubleFunction<T> function) {
this.fromDoubleFunction = function;
}
public boolean add(double value) {
return this.add(this.fromDoubleFunction.apply(value));
}
//rest of code
}
That can then be used in this way:
myList<Integer> intList = new myList(d -> Double.valueOf(d).intValue());
Provide a DoubleFunction<T> to your class as a constructor parameter.
public class myList<T extends Number> extends Collection<T> {
private final DoubleFunction<T> theDoubleFn;
public myList(DoubleFunction<T> theDoubleFn) {
this.theDoubleFn = theDoubleFn;
}
// ...
Then invoke:
return this.add(theDoubleFn.apply(value));
in the method.

Bound Mismatch on new object of generic class

I have an error occuring with a program for an assignment. In it, I have to create an own generic
public class LinkedList<E extends Comparable<T>> implements List<E> {
the implemented Interface is:
public interface List<E extends Comparable<T>> { }
Now, whenever I try to create a new object of the type LinkedList as follows:
LinkedList<Termin> k = new LinkedList<Termin>();
eclipse gives me the following error:
Bound mismatch: The type Termin is not a valid substitute for the bounded parameter > of the type
LinkedList
the class declaration of the class Termin is as follows:
public class Termin implements Comparable<T> { }
in case you need the constructor and variables of the LinkedList object:
private E item;
private LinkedList<E> next;
//Constructor
public LinkedList() {
item = null;
next = null;
}
With a little google magic, I also found out that there once was a bug involving generics in eclipse that gave the same error for no reason.
I suppose some of my declarations aren't entirely correct.
The way the code is written, it cannot compile for a number of reasons, including the lack of specification of the T type, which is not explicitly declared.
One solution is to remove T and replace it with a known Java type (e.g., Object).
A more generic solution is to include T, which means two generic types have to be used.
For the latter case, the code could be something like:
// LinkedList class
public class LinkedList<T, E extends Comparable<T>> implements List<T, E> {
private E item;
private LinkedList<T, E> next;
// Constructor
public LinkedList() {
item = null;
next = null;
}
public static void main(String[] args) {
// Example statement, where T = Long.class and E = String.class
LinkedList<Long, Termin<String>> k = new LinkedList<Long, Termin<String>>();
}
}
and
// List interface
public interface List<T, E extends Comparable<T>> { }
and
// Termin class
public class Termin<T> implements Comparable<T> {
#Override
public int compareTo(T o) {
return 0; // Actual comparison needs to be implemented
}
}

Java - Generics wildcard issue

My goal is pretty simple. I've a SetInterface<T>:
public interface SetInterface<T>
{
public T duplicateSet();
}
I then also have an ExerciseInterface <T extends SetInterface<?>>
public interface ExerciseInterface <T extends SetInterface<?>>
{
public T getSet(int pos);
public void addSet(T set);
}
Everything works great up to this point.
The problem comes where I try to create a Generic Collection containing different classes which all implement the ExerciseInterface.
I am fairly new to Generics and I keep thinking I have solved something but all I am doing is pushing the error about.
The problem is pretty much summed by my addA() and addB() methods
public class Workout {
private String name;
private List <? extends ExerciseInterface<SetInterface<?>>> exerciseList;
// Constructor
public Workout(String name)
{
this.name = name;
exerciseList = new ArrayList<ExerciseInterface<SetInterface<?>>>();
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void addExerciseA(ExerciseInterface<SetInterface<?>> exercise {
exerciseList.add(exercise);
}
public <T extends ExerciseInterface<SetInterface<?>>> void addExerciseB(T exercise {
exerciseList.add(exercise);
}
public ExerciseInterface<SetInterface<?>> getExercise(int pos) {
return exerciseList.get(pos);
}
}
The first add() gives the following error..
The method add(capture#2-of ? extends ExerciseInterface<SetInterface<?>>) in the type List<capture#2-of ? extends ExerciseInterface<SetInterface<?>>> is not applicable for the arguments (ExerciseInterface<SetInterface<?>>)
The second add() gives this error..
The method add(capture#2-of ? extends ExerciseInterface<SetInterface<?>>) in the type List<capture#2-of ? extends ExerciseInterface<SetInterface<?>>> is not applicable for the arguments (ExerciseInterface<SetInterface<?>>)
Just to reiterate, I have a SetInterface that different types of Sets implement.
I also have different exercises which mirror the Sets. For example:
BodyWeightExercise implements <SetInterface<BodyweightSet>>
and
WeightsExercise implements <SetInterface<WeightsSet>>
So my goal is to have a Collection of different exercises and a method that allows me to add to that collection.
Any help would be sincerely appreciated, I have lost a few hairs to this already.
Cheers.
EDIT: If this is not doable any pointers in a more feasible direction would be just appreciated.
EDIT2: I changed the List to List <ExerciseInterface<SetInterface<?>>> exerciseList; and the error on the add() methods both dissappear.
However, using this test code..
WeightsSet weightsSet = new WeightsSet();
WeightsExercise weightsExercise = new WeightsExercise();
weightsExercise.addSet(weightsSet);
Workout workout = new Workout("Wok");
workout.addExerciseA(weightsExercise); <-- ERROR
I am trying to add a WeightsExercise to Workout's List, but I get this error now..
Bound mismatch: The generic method addExerciseB(T) of type Workout is not applicable for the arguments (WeightsExercise). The inferred type WeightsExercise is not a valid substitute for the bounded parameter <T extends ExerciseInterface<SetInterface<?>>>
EDIT 3: Example classes extending ExerciseInterface and SetInterface (Concrete methods update in the interfaces above as well)
public class WeightsExercise implements ExerciseInterface<SetInterface<WeightsSet>>
{
#Override
public SetInterface<WeightsSet> getSet(int pos) {return null;}
#Override
public void addSet(SetInterface<WeightsSet> set) {}
}
public class WeightsSet implements SetInterface<WeightsSet>
{
#Override
public WeightsSet duplicateSet() {return null;}
}
SOLVED: This solution seems to encompass everything correctly.
public class Workout
{
private String name;
private List <ExerciseInterface<? extends SetInterface<?>>> exerciseList;
// Constructor
public Workout(String name)
{
this.name = name;
exerciseList = new ArrayList<ExerciseInterface<? extends SetInterface<?>>>();
}
public String getName() {return name;}
public void setName(String name) {this.name = name;}
public void addExerciseA(ExerciseInterface<? extends SetInterface<?>> exercise) {exerciseList.add(exercise);}
public ExerciseInterface<? extends SetInterface<?>> getExercise(int pos) {return exerciseList.get(pos);}
}
You should change your list declaration to:
List<ExerciseInterface<SetInterface<?>>> exerciseList;
When you use upper bounded wildcards, which I don't think you really need here, you can't add anything to the list, apart from null, and elements previously fetched from it.
That's because, you don't know exactly what concrete parameterized type of list it actually references. For e.g.: List<? extends CharSequence> can reference a ArrayList<String> or ArrayList<StringBuffer>. So, you can't add anything to that list, because it's not type safe. You might possibly be adding a StringBuffer object to ArrayList<String>, that will fail at runtime.
After update:
I guess you are trying to create a parallel class hierarchy, and got messed up in that. Perhaps, you wanted your ExerciseInterface to look like:
// Notice the use of `T` in `SetInterface<T>` instead of `SetInterface<?>`
interface ExerciseInterface<T extends SetInterface<T>> {
SetInterface<T> getSet(int pos);
void addSet(SetInterface<T> set);
}
and then your class WeightsExercise would be modified as such:
class WeightsExercise implements ExerciseInterface<WeightsSet> {
#Override
public SetInterface<WeightsSet> getSet(int pos) {return null;}
#Override
public void addSet(SetInterface<WeightsSet> set) {}
}
You would also need to make your Workout class generic like this:
public class Workout<U extends SetInterface<U>, T extends ExerciseInterface<U>> {
}
Then replace all occurrence of ExerciseInterface<SetInterface<?>> with ExerciseInterface<U> in your Workout class.
And create the instance of Workout like this:
Workout<WeightsSet, WeightsExercise> workout = new Workout<>("Wok");
workout.addExerciseA(weightsExercise);
This will what make a correct design IMO.
Recommended post:
What is a difference between <? super E> and <? extends E>?
What is PECS (Producer Extends Consumer Super)?
References:
Java Generics FAQ - Angelika Langer
What is bounded Wildcards?
Do generics help designing parallel class hierarchies?

Java: generics method and type identification

I'm not very used to generics, so I'm a little confused here about how I'm supposed to solve this problem. I've written a method that tries to call different methods at runtime. But I'm getting a ClassCastException although the code seems syntactically correct.
I have the following classes (some getters and setters were omitted for brevity):
public interface Transporte extends Serializable {
private int id;
private String name;
public abstract int getId() { return this.id; }
public abstract String getName() { return this.name; }
}
public class Barco implements Transporte { /* ... */ }
public class Estacao implements Transporte { /* ... */ }
public class Paragem implements Transporte { /* ... */ }
public class Entidade extends Serializable {
private List<Barco> barcos;
private List<Estacao> estacoes;
private List<Paragem> paragens;
public List<Barco> getBarcos() { return this.barcos; }
public List<Estacao> getEstacoes() { return this.estacoes; }
public List<Paragem> getParagens() { return this.paragens; }
}
And the method I'm trying to implement and have difficulties with:
public <T extends Transporte> List<T> intersectTransportes(Entidade entidade, List<T> transportes) {
if(entidade==null || transportes==null) return null;
T typeOfTransport = (T) new Object(); /* <--- HERE'S THE PROBLEM (?) */
List<T> result = new ArrayList<T>();
List<Integer> ids = null;
if(typeOfTransport instanceof Barco) ids = entidade.getIdBarcos(); else
if(typeOfTransport instanceof Estacao) ids = entidade.getIdEstacoes(); // else ...
// now do the work
for(Transporte t : transportes) {
for(Integer id : ids) {
if(t.getId()==id) result.add((T) t);
}
}
return result;
}
Please notice that I'm using <T extends Transporte> instead of <T implements Transporte> as I'd expect Java to allow. But that latter syntax is invalid, so I have to use implements instead...
The method is being invoked as illustrated here:
List<Estacao> allStations;
List<Estacao> myStations = intersectTransportes(entidade, allStations);
What I'm trying to do here is to identify the actual type used at runtime when invoking the method. In this case, insersectTransportes should be able to recognize the particular List of Transporte-implementing objects I'm using.
I suspect that I'm supposed to use something other than
T typeOfTransporte = (T) new Object();
since obviously that's the line where the runtime exception is being produced. However, I'm not quite sure how to solve this. Any indications to the solution (or specific bibliography approaching this problem) is appreciated.
The problem is:
a) you can't new your generic type - it's erased at runtime
b) Object does not extend Transporte, so it cant be cast to T
You need to pass the class to your method:
public <T extends Transporte> List<T> intersectTransportes(Entidade entidade, List<T> transportes, Class<T> clazz) {
...
T typeOfTransporte = clazz.newInstance();
T typeOfTransport = (T) new Object(); /* <--- HERE'S THE PROBLEM (?) */
The problem is rather obvious, an object is not a "Type of Transport" (does not implement or extends the Transport interface).
You will get the same error with this example:
String myStr = (String) new Object();
T typeOfTransport = (T) new Barco(); //new Barco() or anything that implements Transport interface
You must instantiate a specific class there, but not any class like Object. The class must implement Transport interface.
Of course there is a problem with (T) new Object(). When you call new Object() it creates instance of class Object, thats it. You cant just cast it to something useful.
Try writing your method like this:
public <T extends Transporte> List<T> intersectTransportes(Entidade entidade, List<T> transportes, Class<T> clasz)
and use reflection.
You should pass in a Factory that deals with creating the new Transporte, with a method (probably using a switch) createNew(Class<? extends T> transporteClass).
You should also look into using either commons-collections or the guava libraries, which have the kind of intersection methods you're looking for, saving you the trouble of writing this code.

Categories

Resources