Java - Generics wildcard issue - java

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?

Related

Java generics cast and usage

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.

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.

Can acceptable type be restricted for a generic method in java?

I have a similar requirement to this question.
I would like to generify a method but restrict the types the acceptable by the generic parameter. Currently what I do is attempt to cast to the acceptable types in the method but seems cumbersome if dealing with more than 2 or 3 types.
EDIT:
The types may not be of the same base class. Apologies for not mentioning this earlier.
For this, You must have a base class so that you can do this.
public class Person {
String name;
List<Profession> professions;
int age;
}
public class Doctor {
String university;
Boolean doctorate;
public void work() {
// do work
}
}
public class Teacher {
List<Grade> grades;
float salary;
public void work() {
// do work
}
}
public class Animal<T> {
T type;
}
So, now if you want to write a method which is generic and applies to all, You can do something like this,
public void doSomething(Animal<T extends Person> human) {
human.work();
}
If the class is not of type Person, it will show a compilation error.
UPD1:
In the case, all the classes do not have a common base class. There is some functionality that makes them unique. By this, we can consider them to have a common function, which we can and should add using an interface.
Let's look at some code,
public class Human implements Growable {
public void grow() {
// human grow code
}
}
public class Plant implements Growable {
public void grow() {
// plant grow code
}
}
public class Table {
// does not grows
}
public class GrowService {
public static void grow(Growable growable) {
growable.grow();
}
}
interface Growable {
public void grow();
}
And by calling the below method, we can achieve this
// Works fine
GrowingService.grow(new Plant());
// throws compilation error
GrowingService.grow(new Table());
Java Generics allow basic wildcards such as <T> but also more specifics like
<T extends Number> which means any type T that is Number or a subclass of it or
<T super Number> which means T can be Number or any superclass of Number all the way up to Object.

Confused with generics and subtyping

So, let's say we have a simple interface
public interface ICopyable<T> {
void copyFrom(T original);
}
Which basically adds a copyFrom method with a parameterized type. And you have an interface extending it:
public interface ISomeObject<T> extends ICopyable<T> {
String getType();
}
The interface isn't adding anything valuable of course apart of it's type, but let's pretend there are some useful methods in it. The idea is still the same - all objects of this interface have a getType() method and they can copy one object of THE SAME TYPE from one to the other.
Now let's have two implementations of this interface, and the second implementation inherits from the first one:
public static class ActualObject1 implements ISomeObject<ActualObject1> {
Object data1;
#Override public void copyFrom(final ActualObject1 original) {
this.data1 = original.data1;
}
#Override public String getType() {
return this.getClass().getSimpleName();
}
}
public static class ActualObject2 extends ActualObject1 {
Object data2;
#Override public void copyFrom(final ActualObject1 original) {
super.copyFrom(original);
// oh no! i've just realized that i'm not copying the ActualObject2!
}
}
So the second's object(ActualObject2) is supposed to extend the ActualObject1, but if it's done this way it can't implement the right "copyFrom" method, as the first class implements the interface ISomeObject for itself only. And it obviously wants to do it somehow to allow copying of ActualObject2's to each other. But how?
It can't just declare implements ISomeObject as it'll clash with it's parent's implementation type.
So you'll want to do something like that maybe?
public static class ActualObject1<T extends ActualObject1> implements ISomeObject<T> {
Object data1;
#Override public void copyFrom(final ActualObject1 original) {
this.data1 = original.data1;
}
#Override public String getType() {
return this.getClass().getSimpleName();
}
}
public static class ActualObject2 extends ActualObject1<ActualObject2> {
Object data2;
#Override public void copyFrom(final ActualObject2 original) {
super.copyFrom(original);
this.data2 = original.data2;
}
}
Basically parameterizing the class1, and class2 specifies itself as a parameter. It all works fine, you can create instances of both types:
ActualObject1 obj1 = new ActualObject1();
However there's one "little" problem - the obj1 has a raw type. The full declaration looks rather silly:
ActualObject1<ActualObject1> obj2 = new ActualObject1<>();
But it works. However the "raw type" nature of this class can bite for example in this scanario:
public static class SomeOtherParameterizedClass<T extends ISomeObject<T>> {
void copyObjects(T obj1, T obj2) {
obj1.copyFrom(obj2);
}
}
So you're creating some random class parameterized by <T extends ISomeObject<T>>. And you can in theory say it like that: <T extends ISomeObject> but then you won't be able to use T in the "copyFrom" safely. In other words - it's a valid class parameterization, it has a point.
But then you can't parameterize it for ActualObject1:
SomeOtherParameterizedClass<ActualObject1> a1 = new SomeOtherParameterizedClass<>();
Yea - doesn't work. Hint:
SomeOtherParameterizedClass<ActualObject2> a2 = new SomeOtherParameterizedClass<>();
Works just fine...
So what's the right approach here? I'm more interested in retaining the type safety as much as possible, as for sure you can just use raw types all the way and don't worry about anything, but it's for the weak! :-)
We're in statically typed language so it's somewhat of an academic question - what's the right way of designing this class hierarchies with generics? Is occasionally using raw-types actually required for those to work?

Categories

Resources