How come, in Java, I can write
List<?> list = new LinkedList<Double>();
but not
List<Container<?>> list = new LinkedList<Container<Double>>();
where Container is just something like
public class Container<T> { ... }
This came up because I have a method that accepts a List<Container<?>>, and I would like to use Arrays.asList to pass arguments to it:
process(Arrays.asList(new Container<Double>(), new Container<Double>()));
But the language doesn't allow this, because it infers the type of Arrays.asList to be List<Container<Double>>, and that is not assignable to List<Container<?>>.
If I add a String-parameterized Container to the call,
process(Arrays.asList(new Container<Double>(), new Container<String>()));
it still doesn't work, because it infers the type List<Container<? extends Serializable & Comparable<?>>> for Arrays.asList. Only if I pass in something that is neither Comparable nor Serializable does it work properly.
Of course, I can put in a cast and get the compiler to shut up, but I'm wondering if I'm doing something wrong here.
Each parameterization must be wildcarded:
List<? extends Container<?>> list = new LinkedList<Container<Double>>();
Edit: I was searching through Angelika Langer's FAQ for an explanation, but didn't find one (it's probably there, but hiding somewhere in the 500 pages).
The way to think about this is that each parameterization is independent, and the compiler will not infer information about the parameterization unless you explicitly say to do so.
Edit: I was searching through Angelika Langer's FAQ for an explanation, but didn't find one (it's probably there, but hiding somewhere in the 500 pages).
Thank you very much for the answer. I found an explanation in the FAQ, and after some squinting at it I think I get it.
http://www.angelikalanger.com/GenericsFAQ/FAQSections/TypeArguments.html#What%20do%20multilevel%20wildcards%20mean?
Consider what would happen if you could.
List<Container<Double>> list1 = new LinkedList<Container<Double>>();
List<Container<?>> list2 = list1; // this shouldn't be allowed, because of the below
Container<String> foo = new Container<String>();
foo.add("hi");
list2.add(foo); // legal, because Container<String> is a subtype of Container<?>
Container<Double> bar = list1.get(0);
Double x = bar.get(0); // type error; it is actually a String object
// this indicates that type safety was violated
However, using List<? extends Container<?>> doesn't have this problem because you cannot put anything (except null) into a list of this type (because there is no type that is guaranteed to be a subtype of "? extends Container<?>").
Related
please help me to understand why this cast is unchecked:
List<? extends String> t= ...;
List<String> t2=(List<String>)t;
It should be totally safe...
I think this has probably already been asked, but I can't find it... too many questions about generics! I use regularly generics, but this doubt has always been there... Thank you in advance.
Edit:
As pointed out, I should have used a non final class.
So:
List<? extends ConfigurationAdapter> t= new ArrayList<DATASOURCE_ConfigurationAdapter>();
List<ConfigurationAdapter> t2=(List<ConfigurationAdapter>)t;
This works. I can't understand why it's unchecked.
I suppose an example can explain the reason of the warning.
But let's use other type, since strings cannot actually have subtypes in Java.
List<Integer> integers = new ArrayList<>(asList(1,2,3,4,5));
List<? extends Number> anyNumbers = integers;
Now, suppose we force the cast that you are doing, imagine that you could even do it without a warning
List<Number> numbers = (List<Number>) anyNumbers;
Now, you could do this:
numbers.add(3.5);
And later, in some other place in your code, somebody is trying to use the original collection like this:
for(Integer myInt: integers) { //ClassCastException
}
You get a ClassCastException because you have now broken the expectations of the type system. The original collection should accept Integers only, but you have managed to fool Java to make it accept any kind of numbers, in this case a double. Of course, when you iterate over the original collection, expecting integers, when you find one that is not actually an integer, your code will fail.
That's why it is logical that the compiler throws a warning, and that's why it is dangerous not to listen to it. And the fact that the String is a final class in Java is the reason why in your particular case you won't probably find any scenarios that finish in this problem I just described. But for other cases, this is a likely possibility.
For String it is totally safe and you can ignore the warning. The same would go for any type bound where the type is a final class type.
For any other type, the type of the value assigned to t might be a List<SubtypeOfTheBound>. In which case, it could not be assigned to a List<Bound>.
More reading:
Is List<Dog> a subclass of List<Animal>? Why aren't Java's generics implicitly polymorphic?
why are these declaration invalid in Java?
List<Number> test = new ArrayList<? extends Number>();
List test = new ArrayList<? extends Number>();
are we not allowed to use wildcard during instantiation. and if the wildcards are only useful for passing them to methods?
and List<Object> test = new ArrayList<Integer>(); is illegal because generics are not covariant correct?
The ? wildcard character means "unknown" not "any". It doesn't make any sense to instantiate a new container of unknown contents, what would you put in there? It can't really be used for anything!
So the declaraion new ArrayList<? extends Number>() Means "some specific thing that extends number, but I don't know what." It does not mean "anything that extends number."
The List<Number> you assigned it to would allow both Double and Integer to be added to it, but the actual contents of a List<? extends Number> might be Float! (or whatever else.)
Consider what would happen in this code if the wildcard worked as an "Any":
List<Integer> listInteger = new ArrayList<Integer>();
listInteger.add(Integer.valueOf(1));
List<? extends Number> listWildCard = listInteger;
listWildCard.add(Double.valueOf(1.0)); //This does not compile
Integer integer = listInteger.get(1);//because this would throw a ClassCastException
Footnote regarding your second example:
Declaring a paramaterized type with no type parameter is called using the Raw Type. This is considered a programming error. The syntax is only legal so that code written before java 5 still compiles. Just don't do it if your scenario isn't backward compatability with pre-java 5.
To understand why it is not allowed to create objects of wildcard parameterized types, you must first understand what's the use of wildcard parameterized types.
Why wildcards?
As you already know that Java generics are invariant. So a List<Number> is not a super class of List<Integer>, even though their type arguments are covariant. So, what if you want such a behaviour in generics too, like having the same reference pointing to different objects? That polymorphic thing, as you would name it. What if you want a single List reference to refer to list of Integer, Float, or Double?
Wildcards to the rescue:
With wildcards, you can achieve the above mentioned behaviour. So, a List<? extends Number> can refer to a List<Integer>, List<Double>, etc. So, the following declarations are valid:
List<? extends Number> numbers = new ArrayList<Integer>();
numbers = new ArrayList<Double>();
numbers = new ArrayList<Float>();
numbers = new ArrayList<String>(); // This is still not valid (you know why)
So what did we change here? Just the reference type of the numbers. Note that generics were introduced in Java for enforcing stronger compile time check. So, it's primarily the compiler's job to decide whether the declaration of a parameterized type is conforming to the rule or not. Without wildcard, compiler shows you error for a List<Number> refering to List<Integer>.
So, wildcards are just a way to introduce co-variance like behaviour into generics. By using wildcards, you increase the flexibility, or you can say, reduce the restriction that compiler enforces. A List<? extends Number> reference tells the compiler that the list can refer to a list of Number or any subtype of Number(Of course, there are lower bounded wildcards too. But that's not the point here. The differences between them is already discussed in many other answers on SO only).
Major uses of wildcard parameterized type you would see with method parameters, where you want to pass different instantiation of a generic type for a single method parameter:
// Compiler sees that this method can take List of any subtype of Number
public void print(List<? extends Number> numbers) {
// print numbers
}
But at runtime, for creating an object you have to give a concrete type. A wildcard - bounded, or unbounded, is not a concrete type. ? extends Number could mean anything that is subtype of Number. So what type of List would you expect to be created when you create a List<? extends Number>?
You can consider this case similar to the reason why you can't instantiate an interface. Because they aren't just concrete.
There is a workaround. Really?
Although this is illegal, you would be surprised to know that there is a workaround, as explained in - Java Generics FAQs. But I really don't think you would ever need that.
When you instantiate a parameterized class, the parameter has to be some known, concrete type. It can be parameterized class even with ? but for inference reasons it has to be concrete. E.g. this is a valid declaration: new ArrayList<List<?>>();
The trick here is that the methods that use the type parameter in the arguments of their signature require the type of the argument to be lower bound. That is, any parameter that you pass in can be cast to the parameter type. Example:
public void fillUp(List<? super T> param)
The fillUp method takes a collection and fills it with T type objects. The param list must be able to handle the T objects so it is declared that the list can contain types that are ancestors of T, T can be safely cast to that type. If T was not a concrete type, like ? extends Number, then it would be impossible to exactly define all ancestors of T.
That's not a valid declaration as it's not a known type. You're not specifying a full type here. new ArrayList<Number> can accept anything that extends Number by subtyping so your use of ? extends Foo is not a valid need.
List<Number> can accept Integer, Long, etc. There's no way to do the equivalent of ? super Foo as it would be semantically meaningless beyond List or List<Object> with a strange artificial restriction.
Your current definition is not true, The generic type should be same in both sides or should be have inheritance relation.
Java generics are not covariant. See, for example, the article Java theory and practice: Generics gotchas by Brian Goetz. Your first example has two problems. First, when you instantiate a type it must be fully specified (including any type parameters). Second, the type parameters must exactly match the left side.
Regarding type covariance (or lack thereof), this is also not legal:
List<Number> test = new ArrayList<Integer>();
despite the fact that Integer extends Number. This also explains why the second example is illegal. A raw type is more or less the same as binding the type parameter to Object, so it would be like:
List<Object> test = new ArrayList<Integer>();
which again fails because generics are not covariant.
As to why the type parameters must be fully specified, the Java Language Specification, §8.1.2 explains the concept:
A generic class declaration defines a set of parameterized types (§4.5), one for each possible invocation of the type parameter section by type arguments.
You can only instantiate an actual type. As long as a type parameter of a generic type is unbound, the generic type itself is incomplete. You need to tell the compiler which specific parameterized type (among the set defined by the generic class) is being instantiated.
As to why generics are not covariant, this was intended to prevent the following sorts of errors:
List<Integer> iTest = new ArrayList<Integer>();
List<Number> test = iTest;
test.add(Double.valueOf(2.5));
Integer foo = iTest.get(0); // Oops!
Do not get confused by the Java inheritance concept and wildcard (e.g. ? here) syntex of Java generic concept. Both are not the same and none of the inheritance rule applies to java generic concept.
Hence Number is not same as ? extends Number
Please note that Java Generic wildcard is intended to tell the compiler about the intended object use. At runtime, it does not exist at all!
If you see generic in java just as a tool to prevent you to make mistakes, you should not go wrong in understanding this concept.
I'm studying for the SCJP/OCPJP and I came across a sample question that seams strange to me.
The sample code instantiated two generic collections:
List<?> list = new ArrayList<?>();
List<? extends Object> list2 = new ArrayList<? extends Object>();
The "correct" answer to the question was that this code would compile but adding to either collection would produce a runtime error.
When I try to compile code like this I just get errors. The Java tutorial does not even show this type of code, it instead usually uses wildcards as a part of an upcast.
Collection<?> c = new ArrayList<String>();
Are the two generic collections above even legitimate code? The second by my logic would only disallow interfaces. The first one looks completely useless. Why use a generic that makes no attempt at control?
Check out the excellent Java generics tutorial PDF. More specifically the section about wildcards contains the answer to your question, and I quote
Collection<?> c = new ArrayList<String>();
c.add( new Object() );
Since we don’t know what the element type of c stands for, we cannot
add objects to it. The add() method takes arguments of type E, the
element type of the collection. When the actual type parameter is ?,
it stands for some unknown type. Any parameter we pass to add would
have to be a subtype of this unknown type. Since we don’t know what
type that is, we cannot pass anything in. The sole exception is null,
which is a member of every type.
If you want to declare Type at runtime, you can do something like this:
public class Clazz1<T> {
private final List<T> list = new ArrayList<T>();
private List<T> getList() {
return list;
}
/**
* #param args
*/
public static void main(String[] args) {
Clazz1<Integer> clazzInt = new Clazz1<Integer>();
clazzInt.getList().add(2);
System.out.println(clazzInt.getList());
Clazz1<String> clazzString = new Clazz1<String>();
clazzString.getList().add("test");
System.out.println(clazzString.getList());
}
}
I answered this somewhat before in this answer. ? cannot be used in the instantiation. I'm not sure why it says the code would compile, none of the java compilers I have used would allow that. You could do what is shown above by the following:
List<?> list = new ArrayList();
That would compile and run, but you couldn't do:
list.add("hello world"); //This wouldn't compile
new produces a concrete instance of an object. The concrete instance can have only one type, including any generics. Knowing this, wildcards cannot work with new.
My question is exactly similar to the one here. Just that, I want to do it in Java.
Suppose, I want to crate a list as:
List<a_type> myList = new ArrayList<a_type>();
The problem is that what a_type would be, is decided on the base of a condition. The two possible types are totally unrelated, having no superclass but Object in common. My problem will be solved if I can store the Type in a variable requiredType and use it as List<requiredType>. I am not sure if I'm just being greedy or foolish or ignorant in wanting this, but that's the situation.
Although not important, but the scenario that explains why I need it is explained here.
PS: Please do not mention type-erasure. Kindly consider the situation as it is. I could have very well myself mentioned it in the question. If you think there are design issues, kindly read the situation here and suggest alternatives.
That's not possible in Java, because of the type-erasure and due to the fact that generics is only limited to compiletime.
e.g.
List<Integer> integers = new ArrayList<Integer>();
During compiletime, when the the type-erasing is done becomes
List integers = new ArrayList();
The generics is only a way to tell the compiler, that the list should only deal with objects of type Integer, and let's it point it out if you have somewhere tried to add something other than Integer for type safety.
The only effective (if “risky”) way I could see to do so, would be something akin to:
List<?> myList = new ArrayList<?> ();
/* … */
final Object o = myList.get (0);
if (o instanceof OneType) {
OneType o1 = (OneType)o;
/* … */
} else if (o instanceof OtherType) {
OtherType o2 = (OtherType)o;
/* … */
} else {
throw new RuntimeException ("unexpected type found in list…");
}
You will probably want to litter that code with #SuppressWarnings
Possibly a better(?) idea would be to implement at least a “marker interface” (without perhaps any methods) in both types, and use that as the commonality.
public interface CanBeAddedToDualTypedList { };
public class OneType implements CanBeAddedToDualTypeList { … };
public class OtherType implements CanBeAddedToDualTypeList { … };
List<CanBeAddedToDualTypedList> myList =
new ArrayList<CanBeAddedToDualTypedList> ()
Edit I also put an answer on your related question, showing a concrete example of using a common interface for that specific case.
Java generic are not available at runtime so not possible with java
Search for "Type Erasure" on google and you will understand it better.
You can store the a_type.class object while that wont help you with generics, it can be used to add additional runtime type checks class.cast(...).
Generics do not support runtime typing, the generic type itself does not exist at runtime. C# has a runtime class for each Generic type List<String> is different from List<int>. In java this is not the case both List<String> and List<Integer> degrade to List at runtime.
AFAIK (never tried it) extending a Generic class stores the type somewhere accessible to reflection class StringList extends List<String>{}, the StringList remembers the Generic type used to extend List.
I have a class that maps incoming messages to matching readers based on the message's class. All message types implement the interface message. A reader registers at the mapper class, stating which message types it will be able to handle. This information needs to be stored in the message reader in some way and my approach was to set a private final array from the constructor.
Now, it seems I have some misunderstanding about generics and / or arrays, that I can't seem to figure out, see the code below. What is it?
public class HttpGetMessageReader implements IMessageReader {
// gives a warning because the type parameter is missing
// also, I actually want to be more restrictive than that
//
// private final Class[] _rgAccepted;
// works here, but see below
private final Class<? extends IMessage>[] _rgAccepted;
public HttpGetMessageReader()
{
// works here, but see above
// this._rgAccepted = new Class[1];
// gives the error "Can't create a generic array of Class<? extends IMessage>"
this._rgAccepted = new Class<? extends IMessage>[1];
this._rgAccepted[0] = HttpGetMessage.class;
}
}
ETA:
As cletus correctly pointed out, the most basic googling shows that Java does not permit generic arrays. I definitely understand this for the examples given (like E[] arr = new E[8], where E is a type parameter of the surrounding class). But why is new Class[n] allowed? And what then is the "proper" (or at least, common) way to do this?
Java does not permit generic arrays. More information in the Java Generics FAQ.
To answer your question, just use a List (probably ArrayList) instead of an array.
Some more explanation can be found in Java theory and practice: Generics gotchas:
Generics are not covariant
While you might find it helpful to
think of collections as being an
abstraction of arrays, they have some
special properties that collections do
not. Arrays in the Java language are
covariant -- which means that if
Integer extends Number (which it
does), then not only is an Integer
also a Number, but an Integer[] is
also a Number[], and you are free to
pass or assign an Integer[] where a
Number[] is called for. (More
formally, if Number is a supertype
of Integer, then Number[] is a
supertype of Integer[].) You might
think the same is true of generic
types as well -- that List<Number>
is a supertype of List<Integer>, and
that you can pass a List<Integer>
where a List<Number> is expected.
Unfortunately, it doesn't work that
way.
It turns out there's a good reason it
doesn't work that way: It would break
the type safety generics were supposed
to provide. Imagine you could assign a
List<Integer> to a List<Number>.
Then the following code would allow
you to put something that wasn't an
Integer into a List<Integer>:
List<Integer> li = new ArrayList<Integer>();
List<Number> ln = li; // illegal
ln.add(new Float(3.1415));
Because ln is a List<Number>, adding
a Float to it seems perfectly legal.
But if ln were aliased with li, then
it would break the type-safety promise
implicit in the definition of li --
that it is a list of integers, which
is why generic types cannot be
covariant.
It is right what cletus said. There is a general mismatch between the usually enforced invariance of generic type parameters vs. covariance of Java arrays.
(Some background: Variance specifies how types relate to each other concerning subtyping. I.e. because of generic type parameters being invariant
Collection <: Collection does not hold. So, concerning the Java type system, a String collection is no CharSequence collection. Arrays being covariant means that for any types T and U with T<:U, T[] <: U[]. So, you can save a variable of type T[] into a variable of type U[]. Since there is a natural need for other forms of variance, Java at least allows wildcards for these purposes.)
The solution (the hack, actually) I often use, is declaring a helper method which generates the array:
public static <T> T[] array(T...els){
return els;
}
void test(){
// warning here: Type safety : A generic array of Class is created for a varargs parameter
Class<? extends CharSequence>[] classes = array(String.class,CharSequence.class);
}
Because of erasure the generated arrays will always be of type Object[].
And what then is the "proper" (or at least, common) way to do this?
#SuppressWarnings(value="unchecked")
public <T> T[] of(Class<T> componentType, int size) {
return (T[]) Array.newInstance(componentType, size);
}
public demo() {
Integer[] a = of(Integer.class, 10);
System.out.println(Arrays.toString(a));
}
Arrays are always of a specific type, unlike how Collections used to be before Generics.
Instead of
Class<? extends IMessage>[] _rgAccepted;
You should simply write
IMessage[] _rgAccepted;
Generics don't enter into it.
IMHO,
this._rgAccepted = (Class<? extends IMessage>[])new Class[1];
is the appropriate way to handle this. Array component types have to be reified types, and Class is the closest reified type to Class<whatever>. It'll work just as you would expect a Class<whatever>[] to work.
Yes, technically it is unchecked and might cause issues later if you cast it to another more general array type and put wrong stuff in it, but since this is a private field in your class, I presume you can make sure it is used appropriately.