List? extends MyType
You cannot do a "put" with extends . Look at Generics - Get and Put rule.
Filling a List? extends Foo
The only safe way is by adding a Class
parameter to support type checking at runtime:
private <T extends Foo> List<T> getFooesByType(EnumType type, Class<T> type) {
List<T> result = new ArrayList<>();
for(Foo f : foes) {
if(f.getType() == type) {
switch(type) {
case TYPE1:
// do something, cast is not necessary
break;
case TYPE2:
/*...*/
}
result.add(type.cast(f)); // safe cast
}
}
return result;
}
to be called like
List<FooType1> foesType1 = getFooesByType(TypeEnum.TYPE1, FooType1.class);
This is the only way to ensure that the list elements are of the caller specified type. On the other hand, this makes the requirement that T
is a subtype of Foo
obsolete.
You could change <T extends Foo>
to <T>
raising the flexibility of the method,
e.g. List<Object> list = getFooesByType(TYPE1, Object.class);
would be legal as well.
? super E and ? extends E for List
"? extends A" means "some type derived from A (or A itself)". So for instance, a List<ByteArrayOutputStream>
is compatible with List<? extends OutputStream>
- but you shouldn't be able to add a FileOutputStream
to such a list - it's meant to be a List<ByteArrayOutputStream>
! All you know is that anything you fetch from the list will be an OutputStream
of some kind.
"? super A" means "some type which is a superclass of A (or A itself)". So for instance, a List<OutputStream>
is compatible with List<? super ByteArrayOutputStream>
. You can definitely add a ByteArrayOutputStream
to such a list - but if you fetch an item from the list, you can't really guarantee much about it.
See Angelika Langer's Generics FAQ for much more information.
List? extends Object what I can store into it?
even though while declaring, java allowed me to give new
ArrayList<Integer>
.
Yes. However, it also allows you to assign an ArrayList<String>
.
Since you don't know what the list is supposed to store, you can't add anything (except null
).
how to instanceof ListMyType?
That is not possible because the datatype erasure at compile time of generics. Only possible way of doing this is to write some kind of wrapper that holds which type the list holds:
public class GenericList <T> extends ArrayList<T>
{
private Class<T> genericType;
public GenericList(Class<T> c)
{
this.genericType = c;
}
public Class<T> getGenericType()
{
return genericType;
}
}
How to return a list of generic type T that extends something
It is dangerous to say a list holds items of type B
and allow an item of type C
to exist inside of it. It is safer to say that the list contains an interface or super-class instead.
Here is a dangerous example that throws a runtime exception.
import java.util.*;
public class Main {
public static class A {
public String getA() { return "A"; }
@Override public String toString() { return getA(); }
};
public static class B extends A {
public String getB() { return "B"; }
@Override public String toString() { return getB(); }
}
public static class C extends A {
public String getC() { return "C"; }
@Override public String toString() { return getC(); }
}
public static void main(String[] args) {
List<B> ls1 = new Main().method3();
List<C> ls2 = new Main().method3();
// Will work, because toString() for each class is handled by each class.
System.out.println(ls1);
System.out.println(ls2);
// Runtime errors!
ls1.stream().map(B::getB).forEach(System.out::println);
ls2.stream().map(C::getC).forEach(System.out::println);
}
public static <T extends A> List<T> method3() {
return Arrays.asList((T) new A(), (T) new B(), (T) new C());
}
}
The type of your list should be a common ancestor e.g. List<A> ls1
so that you can safely call common methods as you iterate through the list.
public static void main(String[] args) {
List<A> ls3 = new Main().method3();
ls3.stream().map(A::toString).forEach(System.out::println);
}
Adding data to generic ? extends list
I guess that
? extends B1
means any type that extends from B1.
No. It means "a specific, but unknown, type that extends from B1". As the specific type is unknown, the compiler cannot enforce it, therefore operations like add
don't work.*
See the tutorial on wildcards.
how to make it so that it can be added?
Basically, don't use wildcards for this. You probably want this:
List<B1> l3 = new ArrayList<B1>();
* Well, they do work, but only for
null
(and a few other cases, see @Marko's comment below). Compile-time error using List.addAll to add a List? extends BaseClass to another one
You could make a new List<BaseClass>
and add both lists to that:
List<BaseClass> merged = new ArrayList<>();
merged.addAll(list1);
merged.addAll(lise2);
But it might be a better idea to have getList
return a List<BaseClass>
.
Related Topics
How to I Output Org.W3C.Dom.Element to String Format in Java
How to Change the Default Application Icon in Java
How to Improve the Performance of G.Drawimage() Method for Resizing Images
Guessing the Encoding of Text Represented as Byte[] in Java
Factorial Using Recursion in Java
String Replacement in Java, Similar to a Velocity Template
How to Get the Http Status Code Out of a Servletresponse in a Servletfilter
Is There a Java Equivalent to C#'s 'Yield' Keyword
Which Loop Has Better Performance? Why
Why Dispatcherservlet Creates Another Application Context
Read Error Response Body in Java
Fastest Way to Iterate an Array in Java: Loop Variable VS Enhanced for Statement
Export PDF Pages to a Series of Images in Java