Can't Add Value to the Java Collection with Wildcard Generic Type

Can't add value to the Java collection with wildcard generic type

It's doing that for the sake of safety. Imagine if it worked:

List<Child> childList = new ArrayList<Child>();
childList.add(new Child());

List<? extends Parent> parentList = childList;
parentList.set(0, new Parent());

Child child = childList.get(0); // No! It's not a child! Type safety is broken...

The meaning of List<? extends Parent> is "The is a list of some type which extends Parent. We don't know which type - it could be a List<Parent>, a List<Child>, or a List<GrandChild>." That makes it safe to fetch any items out of the List<T> API and convert from T to Parent, but it's not safe to call in to the List<T> API converting from Parent to T... because that conversion may be invalid.

How can elements be added to a wildcard generic collection?

Use this instead:

1  public List<? extends Foo> getFoos()
2 {
3 List<Foo> foos = new ArrayList<Foo>(); /* Or List<SubFoo> */
4 foos.add(new SubFoo());
5 return foos;
6 }

Once you declare foos as List<? extends Foo>, the compiler doesn't know that it's safe to add a SubFoo. What if an ArrayList<AltFoo> had been assigned to foos? That would be a valid assignment, but adding a SubFoo would pollute the collection.

Why I cannot use wildcard type in parameter to compute

The first method does not compile because of "capture conversion", that happens at each declaration. You can read my other answer about this, or this one. But to put it simply, you will have two separate types there, which you can see when you compile via:

 javac --debug=verboseResolution=all

And the output will contain:

.....
CAP#1 extends Object from capture of ?
CAP#2 extends Object from capture of ?
...

which means there are two types that have been capture converted. These types are unrelated to each other, the way you have it.

This on the other hand:

public static <T> void nothing(Collection<T> c){

}

is called wildcard capture method (it "captures" a wildcard) and is documented in official tutorial on why it works and how; thus you have no problem with that.


But the main problem here is that you can't assign anything (other than null) to a wildcard. So in your compute example, the first argument is going to be inferred to a ? and you can't assign anything to that.

Java: Difficulty assigning to generic from wildcard generic

Generics are strictly checked by the compiler. So, as far as you maintain each generic instantiation related onyl to other instatiations for the same type, everything is fine:

MyGeneric<String> s1=...
MyOtherGeneric<String> o1=...
s1.doSomething(o1);

The problem comes when you try to mix types:

MyGeneric<String> s1=...
MyOtherGeneric<Double> o1=...
s1.doSomething(o1); // Error

Ok, this is pretty obvious. In your case, the problem is similar: You try to collect several generic instantiations no-matter-which-type-they-are-based-on into a non-generic abstraction VarsMap.

So, when you try to mix up an object contained in the non-generic abstraction VarsMap with a generic object Class<T>, how can the compiler determine if their types match or not? Then a compiler error arises.

The simplest solution I see: Since you are already matching the UserVariable objects by name, and I suppose that each variable has always the same type, you can rely in this program-made checking and assume that their types will match: Simply add a method-scoped parameter to the method getVariable and a simple cast:

public <T extends Comparable<T>> UserVariable<T> getVariable(String name)
{
return (UserVariable<T>)this.userVars.get(name);
}

... and also add the proper type parameter substitution wherever you call it:

this.myVarType=theVars.<T> getVariable(variableName).getType();

Generics - Cannot add to a List with unbounded wildcard

That declares that it's a list of something that's a supertype of Integer, not that the list can contain anything that's a supertype of Integer. In other words, to the compiler, it could be a List<Integer>, a List<Number> or a List<Object>, but it doesn't know which, so you can't add just anything to the List. The only thing you can safely add is an Integer, since that's guaranteed to be a subtype of any type the List could potentially hold.

In other words, the ? represents one type, not any type. It's a non-obvious but important distinction.

Java Generics Wildcard confusion

Box<?> box = new Box<String>("abc");
box.put("xyz"); // error
String s = box.take(); // error
  1. In OOP, you're using polymorphism. So at compiling time, type of box object is Box<?>

  2. Type<?> This is called unknown wildcard. Because you don't know what type of Box is typed to, you can only read from that object, and you can only use the objects read as being Object instances. That's why box.put("xyz") is received error and String s = box.take() is received error.

Secondly:

boolean equal = box.equalTo(box);

The equalTo is received Box<T> not Box<?>. As I explained above, T stand for any class, but ? just meaning unknown wildcard, those two terms aren't the same.

And other point you should know. In Java, generic is at compile-time. Contrast to other such as C#, generic is at run-time.

Here is a reference link about wildcard: Java wildcard

Hope this help :)

In Java, what can a wild card do that regular generics cannot do?

One thing wildcards allow us to do is declare types that are agnostic towards a particular type parameter, for example a "list of any kind of list":

List<List<?>> listOfAnyList = ...;

listOfAnyList.add( new ArrayList<String>() );
listOfAnyList.add( new ArrayList<Double>() );

This is impossible without a wildcard:* because the element lists may have different types from each other.

And if we try to capture it, we will find that we can't:

static <E> void m(List<List<E>> listOfParticularList) {}

m( listOfAnyList ); // <- this won't compile

Another thing wildcards allow us to do that type parameters cannot is set a lower bound. (A type parameter can be declared with an extends bound, but not a super bound.**)

class Protector {
private String secretMessage = "abc";

void pass(Consumer<? super String> consumer) {
consumer.accept( secretMessage );
}
}

Suppose pass was instead declared to take a Consumer<String>. Now suppose we had a Consumer<Object>:

class CollectorOfAnything implements Consumer<Object> {
private List<Object> myCollection = new ArrayList<>();

@Override
public void accept(Object anything) {
myCollection.add( anything );
}
}

The problem is: we can't pass it to a method accepting Consumer<String>. Declaring Consumer<? super String> means that we can pass any consumer which accepts a String. (Also see Java Generics: What is PECS?.)

Most of the time, wildcards just let us make tidy declarations.

If we don't need to use a type, we don't have to declare a type parameter for it.


* Technically also possible with a raw type, but raw types are discouraged.

** I don't know why Java doesn't allow super for a type parameter. 4.5.1. Type Arguments of Parameterized Types may hint that it has something to do with a limitation of type inference:

Unlike ordinary type variables declared in a method signature, no type inference is required when using a wildcard. Consequently, it is permissible to declare lower bounds on a wildcard […].

Generic Casting Issue

You can hack this a bit, via a so called "wildcard capture method":

private static <T> void process(ItemProcessor<T> item) {
item.processItem(item.getNextItem());
}

And then change your call to:

for (ItemProcessor<?> itemProcessor : itemProcessors) {
process(itemProcessor);
}

I have two answers here and here, for example explaining why this happens. But there is also the official Oracle tutorial explaining things



Related Topics



Leave a reply



Submit