Take a look at the next code:
ArrayList<? extends Object> list = new ArrayList<Object>();
list.add(new Object());
It does not compile. The question is why?
ArrayList<? extends Object>
is the same as just
ArrayList<?>
and you can assign any ArrayList to a variable of this type. For example,
ArrayList<? extends Object> list = new ArrayList<String>();
is legal. Clearly, the language semantics will not let you add an Object to such a list. In fact, they won't let you add anything at all to it, except null, or something which you directly retrieved from the list.
As noted by Lukas in the comment, it is far from trivial to even add the list's own item back to it: you need a helper method to capture the wildcard into a named type.
public static void main(String[] args) {
ArrayList<? extends Object> list = new ArrayList<String>();
addOwn(list);
}
static <T> void addOwn(List<T> l) { l.add(l.get(0)); }
The problem is that Foo extends Object does not mean Collection<Foo> can be treated as a subtype of Collection<Object>. This is simply because the former class does not permit you to do everything the latter does; for instance, you cannot add an Object to a Collection<Foo>. Using generics instead of some concrete class Foo doesn't change this.
I think the reason is because Generic types are not polymorphic. When you use wildcards ? with extends you cant add anything in the collection except null.
Here is an example to what will happen if that is allowed:
Class Car{
}
class A extends Car {
}
class B extends Car{
}
Now you have List<? extends Car>
public void someMethod(List<? extends Car> list){
list.add(new A()); //this is not valid
}
Also you may invoke the method like this:
List<B> someList = new Array:list<B>();
somemethod(someList);
Related
I want to create a list from a Class variable.
...
Class clazz = someObject.getClass();
List<clazz> myList = new ArrayList<clazz>(); // this line is a compilation error
...
How I can do it?
EDIT: I think you are getting it wrong. For example, if someObject is a Car, then myList type should be List<Car>. I want to treat my clazz variable as a type, in some way.
EDIT:
As Boris the Spider pointed out in the comments it actually is possible by using a generic method, but with a slight modification:
Instead of using the normal Class object, use the generic version Class<? extends [Type of someObject]>.
Example:
public static void main(String[] args){
Test t = new Test();
Class<? extends Test> testClass = t.getClass();
List<? extends Test> list = createListOfType(testClass);
}
private static <T> List<T> createListOfType(Class<T> type){
return new ArrayList<T>();
}
Of course you can also just go with
public static void main(String[] args){
Test t = new Test();
List<? extends Test> list = createListOfType(t);
}
private static <T> List<T> createListOfType(T element){
return new ArrayList<T>();
}
OLD POST:
You can't.
You can't, because Java needs to know the generic type you want to use for the ArrayList at compile time. It's possible to use Object as type though.
You can't use variable name as the type. You must use Class instead of clazz
List<Class> myList = new ArrayList<Class>();
Keep in mind about type erasure. The Obvious answer is,
List<Class> myList = new ArrayList<Class>();
You can only specify the type of Object (for compiler). You can't see them at Run Time :)
i want to add element extending Type to ArrayList.
ArrayList<? extends Type>
i defined a method as below
public <T extends Type> void addItem(T itemToAdd, Class<?> classType) {
ArrayList<? extends Type> allItems = getAllItems(classType);
allItems.add(itemToAdd);
}
T itemToAdd is error. because i can't add someother type. i thought but i don't know to mention it and its error!
How to add the item via a method call?
Think about your definition: ArrayList<? extends Type>. It means list of elements each of them extends Type. It does not mean that each of them is of type T as itemToAdd. To make this code to compile you have to ensure this fact, i.e. use T in all relevant places:
public <T extends Type> void addItem(T itemToAdd, Class<T> classType) {
List<T> allItems = getAllItems(classType);
allItems.add(itemToAdd);
}
It probably means that you should change definition of getAllItem() to
public <T extends Type> List<T> getAllItems(Class<T> classType)
BTW please pay attention that I changed your ArrayList to List. Although it is out of the topic but avoid using concrete classes at the left part of assignment operator, as a return value of method and as a method parameter.
If you define for your List all object must have the same type
If you want to insert multiple class defined simple List and you can insert objects extends Type
Example :
List<Type> list = gellAllItems(class);
list.add(T);
I'm not sure about your declaration of the return value of getAllItems, but you can't put itemToAdd with type T extends Type into ArrayList<? extends Type>. The type of element may be unmatch (different type although both are the derived class of Type), so the ArrayList should be declared as
ArrayList<? super Type> allItems;
which can guarantee itmeToAdd can be put into the ArrayList, or just:
ArrayList<Type> allItems;
I have 3 simple classes as follows:
public class ElementA {}
public class ElementB extends ElementA {}
public class ElementC extends ElementB {}
Then if I want to create, for example, generic List which takes only subclasses of ElementA class I can declare it as:
List<? super ElementA> list = new ArrayList<>();
and then use it as follows:
list.add(new ElementA());
list.add(new ElementB());
list.add(new ElementC());
which is fine and can be compiled without errors. But I became confused if I want to store anything but not ElementC or ElementB or ElementA. I declare such List as follows:
List<? extends ElementC> list = new ArrayList<>();
and I can't use it at all because it can store only null values. Same thing happen when I declare List as (notice that I'm using class which is 'in the middle of family'):
List<? extends ElementB>
Why so?
The problem is that the value of ? is not known at runtime. You have to substitute a concrete class/interface in order to be able to do what you want.
If you do this:
List<ElementA> list = new ArrayList<ElementA>();
you are fine since ElementB is an ElementA at the same time. Same stands for ElementC.
List<? extends ElementA> makes sense if you for example declare it in a class and in a subclass you can substitute something concrete as the type parameter. Clumsy example:
public class SomeClass<T> {
private List<? extends T> list;
public void setList(List<? extends T> list) {
this.list = list;
}
}
public class SomeConcreteClass extends SomeClass<Integer> {
public void doSomething() {
List<Integer> list = new ArrayList<Integer>();
setList(list);
}
}
List<ElementA> accepts instances of ElementA, ElementB, and Element C.
List<ElementB> accepts instances of ElementB and Element C.
List<ElementC> accepts instances of ElementC.
There is no reason for the wildcard in your examples.
List<? super ElementA> means a List of some type which is ElementA or a superclass.
List<? extends ElementB> means a List of some type which is a subclass of ElementB. If you get an element it will be ElementB or a subclass, but it doesn't know what the class is, so it can't be sure the element you add is of the right type, since it is unknown (though it does know it to be a subclass of ElementB).
There are uses for wildcard, but your example is not one of them.
You create a List like this
List<? extends ElementC> list = new ArrayList<>();
but let's say, because it's still valid that you got the List like this
List<? extends ElementC> list = getElementCSubclassList(); // declared as returning a `List<ElementCSubclass>`
Now the compiler cannot know that your list object contains ElementCSubclass objects, it can only be sure that it contains some type of ElementC. As such, it can't let you use any methods that expect the actual generic type.
Imagine
public class ElementCSubclass1 extends ElementC {}
public class ElementCSubclass2 extends ElementC {}
...
List<? extends ElementC> list = getElementCSubclass1List(); // declared as returning a `List<ElementCSubclass1>`
list.add(new ElementCSubclass2()); // this would immediately have to fail
Compiler does this so that the previous situation never occurs.
I think I'm asking about covariant return types. I have some generated code that I'm trying to extend and use. Let's suppose I have the following two classes:
public class SuperParent
{
public List<SuperChild> getList()
{
return new ArrayList<SuperChild>();
}
}
public class SuperChild
{
}
Now, I want to derive new classes from these thusly:
public class SubParent extends SuperParent
{
public List<SubChild> getList()
{
return new ArrayList<SubChild>();
}
}
public class SubChild extends SuperChild
{
}
The problem is, apparently I can't override the getList() method because the return type doesn't match, despite both classes being extended in the same direction. Can someone explain?
Your understanding of co-variant is correct but usasge is not. List<SubChild> is not the same as List<SuperChild>
Consider this, List<Animals> is not the same as List<Dogs> and things can go horribly wrong if that was allowed. A Dog is an Animal but if it was allowed to assign like below:
List<Dogs> dogs = new ArrayList<Dogs>();
List<Animals> animals = dogs; //not allowed.
then what happens when you add a cat to it?
animals.add(new Cat());
and
Dog dog = dogs.get(0); //fail
So its not allowed.
As sugested by many others, use List<? extends SuperChild> as return type to solve your problem.
EDIT
To your comment above, if you do not have control over super class, i am afraid, you can not do anything.
The problem is that with generics List<SuperChild> and List<SubChild> are not compatible, since if you'd call getList() on a SubParent instance but through a SuperParent interface, you'd get a return value of type List<SuperChild>. This would allow you to add other instances of SuperChild even though the list is only allowed to contain instances of SubChild (as per the return type defined in SubParent).
To make this compile change the return type to List<? extends SuperChild>, i.e.
public class SuperParent
{
public List<? extends SuperChild> getList()
{
return new ArrayList<SuperChild>();
}
}
This would allow you to return lists of subtypes but would not allow you to add elements to the list returned using the super type (i.e. you can't add elements to a List<? extends SuperChild>.
List<SubChild> is not an subclass of List<SuperChild>
There is no co-variance in java's generics.
So, when you try to co-variant the return type, it is actually a different type, and java does not allow you to change it completely [since it will not be safe].
Your method getList() in SubParent should return List<SuperChild> [or ArrayList<SuperChild>, ...] to solve this issue.
As others pointed out List<SubChild> is not a subclass of List<SuperChild>.
Depending on what you want to do, you could use generics:
public class SuperParent<T extends SuperChild>
{
public List<T> getList()
{
return new ArrayList<T>();
}
}
public class SuperChild
{
}
public class SubParent extends SuperParent<SubChild>
{
public List<SubChild> getList()
{
return new ArrayList<SubChild>();
}
}
public class SubChild extends SuperChild
{
}
Imagine something like this:
SubParent subParent = new SubParent();
SuperParent superParent = (SuperParent) subParent; // upcast is okay
List<SuperChild> list = superParent.getList();
list.add(new SuperChild());
The last statement would violate the contract of SubParent.
A fix would be to change the contract of SuperParent's getList to List<? extends SuperChild> getList().
I've been trying to extend the ArrayList class without much success. I want to extend it, and be able to parameterize it.
So normally you have something like
ArrayList<SomeObject> list = new ArrayList<SomeObject>();
I want
MyList<SomeObject> list = new MyList<SomeObject>();
Simply extending ArrayList doesn't work.
public class MyList extends ArrayList ...
The when I try to use it I get the error
The type MyList is not generic; it
cannot be parameterized with arguments
<SomeObject>
I've tried variations of
public class MyList extends ArrayList<Object>
public class MyList<SubObject> extends ArrayList<Object>
with no success, If I use the subobject behind the class name it appears to work, but hides methods in the subobject class for some reason.
Any thoughts or suggestions on how to get this working right are appreciated.
You need to specify a type for the ArrayList's type parameter. For generic type parameters, T is fairly common. Since the compiler doesn't know what a T is, you need to add a type parameter to MyList that can have the type passed in. Thus, you get:
public class MyList<T> extends ArrayList<T>
Additionally, you may want to consider implementing List and delegating to an ArrayList, rather than inheriting from ArrayList. "Favor object composition over class inheritance. [Design Patterns pg. 20]"
public class MyList<T>
extends ArrayList<T>
{
}
MyList<SomeObject> list = new MyList<SomeObject>();
or
public class MyList
extends ArrayList<SomeObject>
{
}
MyList list = new MyList();
You shouldn't extend ArrayList, extend AbstractList instead:
public class MyList<T> extends AbstractList<T> {
public int size() {...}
public T get(int index) {...}
}