Java generics: what is the difference between <?> and skipping it? - java

Maybe it is silly question: Let say I have
abstract class A<T> {
List<Wrapper<T>> doStuff()
}
And I have class B extends A<String> and class C extends A<Integer>
Now I want to have:
List<A> aces = list with instances of B and C;
List<Wrapper> wrapperedItems = flattened list of lists returned from doStuff() on all items in aces
At this point I don't care what type is within Wrapper.
And my question is: shall I use somewhere <?> or can I skip it? What is the difference?

If you use List<Wrapper<?>>, you will be able to get objects out of the list, but you will not be able to add new items. The wildcard essentially correcponds to a type that is different from any other type, including other wildcards.
If you use List<Wrapper>, you will be able to both get list items and add new ones, but the onus is now up to you to guard for improper type casts.

Related

Java Generics & Inheritance: Unable to add to List

Suppose I have a List definition as follows.
private List<? extends GeneralBudgetYearBean> budgetYearBeans;
// + Getters/setters...
Why am I unable to generally refer to this List as follows outside this class:
GeneralBudgetYearBean gbyb;
getBudgetYearBeans().add(bgyb);
The error is:
The method add(capture#6-of ? extends GeneralBudgetYearBean) in the type List<capture#6-of ? extends GeneralBudgetYearBean> is not applicable for the arguments (GeneralBudgetYearBean)
This doesn't make sense. Is it because "? extends T" is not the same as "T", and "T" can't be substituted here?
I need to be able to generally manipulate this class without specifics. At some point, there will be an ArrayList of "SUBGeneralBudgetYearBeans" constructed in extending classes, which is why I need to use the "? extends GeneralBudgetYearBean" notation.
According to the PESC principle:
Use extends when only need to get objects and super when only need to add object.
In your case, the
private List<? extends GeneralBudgetYearBean> budgetYearBeans;
is a producer, which means that the list will only be able to produce elements. The reason is that at compile-time the compiler is not aware of the sub-type of GeneralBudgetYearBean, which is why it doesn't let you add elements, since it's not entirely sure whether to allow you or not.
Since you will (at some point) also add SubBudgetYearBean objects (which I believe are subclasses of GeneralBudgetYearBean), you will have to create the list as a consumer. Thus, this will work for you:
private List<? super SubBudgetYearBean> budgetYearBeans;
In this case, you will be able to instantiate budgetYearBean as:
budgetYearBeans = new ArrayList<Object>();
budgetYearBeans = new ArrayList<GeneralBudgetYearBean>();
In both of the cases, you will be able to add both GeneralBudgetYearBean and SubBudgetYearBean objects.
When you use the expression List<? extends GeneralBudgetYearBean> budgetYearBeans;, you tell the compiler that the variable will later receive a List where X will extend GeneralBudgetYearBean. But it cannot know what class it will be, and as such if will not allow you to add anything into it.
You can make the current class generic :
class xxx<T extends GeneralBudgetYearBean> {
private List<T> budgetYearBeans; // getter and setter
Then you will be allowed to do :
T gbyb;
getBudgetYearBeans().add(bgyb);
because now you tell the compiler : you do not know exactly what it will be, but it will be the same thing.
Drop the wildcards, they won't really help you in this case, they just complicate things unless you really know what you are doing.
private List<GeneralBudgetYearBean> budgetYearBeans = ...
public List<GeneralBudgetYearBean> getBudgetYearBeans() {
return budgetYearBeans;
}
Because SUBGeneralBudgetYearBean extends GeneralBudgetYearBean, then there is no problem with either adding them to the list by one...
SUBGeneralBudgetYearBean sgyb = ...
getBudgetYearBeans().add(sgyb);
... or as a list...
List<SUBGeneralBudgetYearBean> sgybs = ...
getBudgetYearBeans().addAll(sgybs);
See? No wildcards are actually needed. :)

Why generic type is not applicable for argument extends super class for both?

Here is the problem that I have been being tried to find the solution.
We have two class definitions. One of two extends other one.
class T{}
class TT extends T{}
The requirement is that there should be a list keeps object extends T
List<? extends T> list = new ArrayList<>();
But the problem occures when I try to put a TT object ( barely seems it is a subclass of T )
into the list.
list.add(new TT());
Compilation Error Message
The method add(capture#2-of ? extends Cell) in the type List is not applicable for the arguments (Cell)
You can create a List<T> list = new ArrayList<T>(); directly, this can allow all subtypes of T into the list. This is actually little difficult to understand. when you declare it as
List<? extends T> list = ...
It means that it can allow any unknown subtypes of T into the list. But, from that declaration we cannot ensure which is the exact sub-type of T. so, we can only add null into it
List<? extends T> indicates that anything can comes out of it can be cast to T, so the true list could be any of the following:
List<T>
List<T2>
List<TT>
etc
You can see that even a new T cannot safely be added to such a collection because it could be a List<T2> which T cannot be put into. As such, such List cannot have non null entries added to them.
In this case you may simply want List<T>
So why would you ever use this?!
This contravariance can be useful for method parameters or returns, in which a collection will be read, rather than added to. A use for this could be to create a method that accepts any collection that holds items that are T, or extend T.
public static void processList(Collection<? extends Vector3d> list){
for(Vector3d vector:list){
//do something
}
}
This method could accept any collection of objects that extends Vector3d, so ArrayList<MyExtendedVector3d> would be acceptable.
Equally a method could return such a collection. An example of a use case is described in Returning a Collection<ChildType> from a method that specifies that it returns Collection<ParentType>.
The requirement is that there should be a list keeps object extends T
If you just want a List where you can store objects of any class that extend from T, then just create a List like this:
List<T> list = new ArrayList<T>();
The way you've created a list currently, will not allow you to add anything except null to it.
There are boundary rules defined for Java Generics when using WildCards
**extends Wildcard Boundary**
List means a List of objects that are instances of the class T, or subclasses of T (e.g. TT). This means a Read is fine , but insertion would fail as you dont know whether the class is Typed to T
**super Wildcard Boundary**
When you know that the list is typed to either T, or a superclass of T, it is safe to insert instances of T or subclasses of T (e.g.TT ) into the list.
In your example , you should use "super"
An addition to the other answers posted here, I would simply add that I only use wild cards for method parameters and return types. They're intended for method signatures, not implementations. When I put a wildcard into a variable declaration, I always get into trouble.

Collection<Sub> casted as SuperCollection<Super>

In java one can cast instance of a class B into an instance of class A provided that B extends A.
Can this be done also for type-parametrized classes? For instance, for B extending A, I'd like to do the following:
List<A> l = new ArrayList<B>();
I think this should be legal, but the compiler doesn't agree with me on this point, so I tricked it with the following hack:
List<A> l = (List<A>)(List) new ArrayList<B>();
…but I think that I'll be hit by a velociraptor. Is there an elegant way of doing this?
I think this should be legal, but the compiler doesn't agree with me on this point
Generally, when humans and compilers disagree, it is safe to take the compiler's side. Instead of tricking the compiler, you should first understand why it does not allow the conversion.
Consider this: you make a List<B>, like this:
List<B> listB = new ArrayList<B>();
Everyone is happy so far. Now let's do what you think is correct:
// This does not compile, but let's pretend that it does
List<A> listA = (List<A>)listB;
At this point, we have listA that is entirely ours! Let's play with it: let's say we have another class C extending A. Can we do this?
listA.add(new C()); // Why not? The compiler should allow it!
But wait, do you see what we have done? Our listB contains a C now! This is why the compiler does not allow the conversion; this is also why your code is not safe in cases when you attempt to add anything to the list.
To work around this issue, you can use wildcards: List<? extends A>. However, this will limit your lists to read-only access. If you must allow writing (and you know that you are going to set only the right stuff) use the non-generic List instead.
List<? extends A> l = new ArrayList<B>();
You should read about generics in java here
Two ways to do it.......
- This way of handling the Collections are done, cause Array is checked during compile and as well as runtime, but Collections are checked only during Compile time.
- So there should NOT be an accidental addition of another type of object in to another type of Collection, ie Dog Object into a Cat Collection.
1.
public <T extends A> void go(ArrayList<T> aList){
}
2.
public void go(ArrayList<? extends A> aList){
}
In a non generic context, if B "is a" A, then B can be substituted for A, but A cannot be substituted for B.
With generics, List and List are both "is a" Collection<E>; that is, List is not a subclass of List, and therefore, you cannot substitute List for List.
The specific content on generics you're looking for can be found here, copied below for your convenience.
Given two concrete types A and B (for example, Number and Integer), MyClass has no relationship to MyClass, regardless of whether or not A and B are related. The common parent of MyClass and MyClass is Object.

Where can we use ArrayList< ? extends My_Class>

Where can we ArrayList<? extends My_Class> because it won't allow you to add any new element if you declare it of this way:
ArrayList<? extends My_Class> obj=new ArrayList<? extends My_Class>();
list.add(new Derived_My_Class()); //this is compilation error
The widely-used acronym for describing these keywords is: PECS: Producer extends - consumer super.
So, if you use "extends" your collection produces elements of the given type. So you can't add to it, you can only take from it.
An example usage would be to provide a client of your API with a list:
public List<? extends CharSequence> getFoo() {
List<String> list = new ArrayList<String>();
list.add("foo");
return list;
}
Related: Java generics: Collections.max() signature and Comparator
You are correct, your ArrayList cannot be used the way it is derived: there is nothing that you can put into it.
What you want for storing My_Class and its subclasses is simply ArrayList<My_Class>; it will take objects of My_Class and all its DerivedClass correctly.
Well...There is no point one will declare ArrayList like this, rather your intentions will be fullfilled by writing
ArrayList< Supertype> obj=new ArrayList< Supertype>();
As per my epxerience, I have seen this notion in method arguments, where you expect your caller to provide a collection of subtypes of particular Supertype (or return from a method likewise as someone said above). like as follows
public getAnimals(List< ? extends Animal> obj){
obj.add(something); //not allowed
}
there are fair chances that you can add donkeys, monkeys and birds etc in your List of certain type say (Monkey). and get classCastException while getting from it.
That's why It not allowed in this case. read Effective Java by Josh Bloch. he has explained it well with producer consumer analogy(PECS)

Java generics confusion

I am a bit of confused about java generics
Here is the code
class Base{}
class Derived extends Base{}
WE can instantiate a list like this
List<? extends Base> list = new ArrayList<Base>();
Why cannot I add a a new item like this
list.add(new Base());
So user cannot use "add" method as far as a wildcard ? in the genetics type?
Thanks
PECS - producer extends, consumer super.
If you replace extends with super, you can add new Base().
List<? extends Base> means "a list that holds instances of any subclass of Base (or Base itself). But it cannot hold instances of two different subclasses.
If you want your list to hold Base and Derived, then use List<Base>. But note that it cannot be later cast to List<Derived>
Just make it
List<Base> list = new ArrayList<Base>();
You shouldn't use wildcards when you know the actual type... just when you're being provided with something with an unknown type.
In such cases, ? extends Base means that the List is only allowed to contain some specific subtype of Base, but you don't know which subtype that is. Because of that, you can't add anything but null to the list.
You can try reading ? as something:
List<? extends Base>
This is "List of something that extends Base". So it is clear that you cannot add a Base (just as you cannot add an Object to a List<String> even when String extends Object.
What you can do in your case is:
List<? super Base>
This is "List of something that is extended by Base". So you can add a Base there (just as you can add a String to a List<Object>, because Object is extended by String.
I think this is a design of Java Generics. The wildcard ? extends Base is compiled to mean that the collection reference can point to a collection object that can hold any ( and all ) types that extend Base.You can write like this as well :
List<? extends Base> _listBaseSubtypes = new ArrayList<Derived>();
Now , with the above line , if you think about it , the below will be obviously an error :
_listBaseSubtypes.add(new Base());
I think Java designers decided to allow the first line of code as valid. In order to avoid the runtime error that the second line of code can cause , it is caught at compile time.
Having said that , the question that comes to mind is : What type of object should be allowed to be added into the collection , given the fact that the actual collection object can be a collection of 'any' derived type ?
Because you can derive as many types as you want , and there cannot be found a single type that is assignment compatible with the type held in the actual collection object ( remember , the collection object could be declared to hold 'any' derived type ) , the simple answer to the question is : None. So , you cannot add any object into the collection through the add interface, because for any object that you may try passing into the add method , there will be complier objection raised on the reason that this type is not compatible with the type that the actual collection object holds.

Categories