Creating an array of generic collections - java

Actually, the question should be
Creating an array of generic anything.
Why can't the compiler take care of it?
The following would be flagged as an error - cannot create generic array.
List<MyDTO>[] dtoLists = {new ArrayList<MyDTO>(), anExistingDtoList};
To overcome that, I need to
List<MyDTO>[] dtoLists = (List<MyDTO>[])Array.newInstance(ArrayList.class, 2);
dtoLists[0] = new ArrayList<MyDTO>();
dtoLists[1] = anExistingDtoList;
So, why can't the compiler convert the first case into the second case?
I do realise that generics are compile-time determinate and not run-time determinate, while arrays are run-time determinate and therefore need a determinate type in order to create an array.
What are the technological/logical barriers compiler designers would encounter that would prevent them being able to implement this?
Is the issue purely philosophical, concerning language orthogonality? If so, how would such a behaviour violate language orthogonality?
Is it a question of complexity? Explain the complexity.
I am hoping answers to my question would give me better insight into java compiler behaviour when it concerns generics.
Side note:
c'mon stop being trigger happy. The answers Array of Generic List
do not answer my question. Why can't compilers spontaneously perform the conversion?

Actually Java does create generic array for varargs, so you can do
List<MyDTO>[] dtoLists = array(new ArrayList<MyDTO>(), anExistingDtoList);
#SafeVarargs
static <E> E[] array(E... array)
{
return array;
}
As to why is explicit generic array creation forbidden, it has something to do with type erasure. (The same concern exists in the above solution, but suppressed by #SafeVarargs) However it is debatable; there are different ways to handle the concern, a compiler warning is probably enough. But they chose to outright ban it, probably because arrays are no longer important anyway now that we have generic collections

I do know that, relative to the workarounds to this issue, Array.newInstance() is an expensive method to call. IIRC it uses a native method to instantiate the array, amidst the other reflection involved. I can't offer any statistics, but this seems like a good enough reason for such functionality not to be automatically substituted in by the compiler in order to allow generic array creation. Especially given the existence of ArrayList, etc. it just doesn't seem like a pressing issue.

Compilers can spontaneously perform the conversion, they are just specified not to because generic arrays can't behave like non-generic arrays.
See 10.5. Array Store Exception:
For an array whose type is A[], where A is a reference type, an assignment to a component of the array is checked at run time to ensure that the value being assigned is assignable to the component.
If the type of the value being assigned is not assignment-compatible with the component type, an ArrayStoreException is thrown.
If the component type of an array were not reifiable, the Java Virtual Machine could not perform the store check described in the preceding paragraph. This is why an array creation expression with a non-reifiable element type is forbidden.
A List<MyDTO>[] would not throw if we put some other kind of List in it, so it doesn't behave as an array. Note the last sentence from the quote: "This is why an array creation expression with a non-reifiable element type is forbidden." This is the reason, it's specified to be so. (And, for the record, this reasoning has always existed, so it was present when the question was posted in 2011.)
We can still do this:
#SuppressWarnings({"unchecked","rawtypes"})
List<MyDTO>[] dtoLists = new List[] {
new ArrayList<MyDTO>(), anExistingDtoList
};
Or this:
#SuppressWarnings("unchecked")
List<MyDTO>[] dtoLists = (List<MyDTO>[]) new List<?>[] {
new ArrayList<MyDTO>(), anExistingDtoList
};
(Besides statically checking the argument types, the varargs thing is equivalent: it creates a List[] and suppresses warnings.)
Now, sure, the specification could be changed to something like "If the type of the value being assigned is not assignment-compatible with the raw type of the component type...", but what is the point? It would save a handful of characters in some unusual situations but otherwise suppress warnings for those who don't understand the implications.
Furthermore, what the tutorial and other typical explanations I've seen don't demonstrate is just how baked in to the type system covariant arrays are.
For example, given the following declaration:
// (declaring our own because Arrays.fill is defined as
// void fill(Object[], Object)
// so the next examples would more obviously pass)
static <T> void fill(T[] arr, T elem) {
Arrays.fill(arr, elem);
}
Did you know that this compiles?
// throws ArrayStoreException
fill(new String[1], new Integer(0));
And this compiles too:
// doesn't throw ArrayStoreException
fill(dtoLists, new ArrayList<Float>());
Before Java 8, we could make those calls to fill fail by giving it the following declaration:
static <T, U extends T> void fill(T[] arr, U elem) {...}
But that was only a problem with type inference, and now it works "correctly", blindly putting List<Float> in to a List<MyDTO>[].
This is called heap pollution. It can cause a ClassCastException to be thrown sometime later, likely somewhere completely unrelated to the actions that actually caused the problem. Heap pollution with a generic container like List requires more obvious unsafe actions, like using raw types, but here, we can cause heap pollution implicitly and without any warnings.
Generic arrays (and really, arrays in general) only give us static checking in the simplest of circumstances.
So it's evident that the language designers thought it was better to just not allow them, and programmers who understand the problems they present can suppress warnings and bypass the restriction.

Related

Create a multidimensional generic array of Optionals

I want to create a two-dimensional array (yes I know that this is actually an array of arrays) holding Optionals. The normal approach for generic array creation does not work though as it fails with a ClassCastException. Here is my code:
#SuppressWarnings("unchecked")
Optional<Integer>[][] arr = (Optional<Integer>[][]) new Object[5][5];
Is there a way to create such an array, if yes what would be the approach for that?
In Java "it is illegal to create an array of a generic type, a parameterized type, or a type parameter". "Why is it illegal to create a generic array? Because it isn’t typesafe. If it were legal, casts generated by the compiler in an otherwise correct program could fail at runtime with a ClassCastException. This would violate the fundamental guarantee provided by the generic type system." [Joshua Bloch - Effective Java]
So what solutions are to be able to create multidimensional arrays?
The recommended one would be to use a container:
List<List<Optional<Integer>>> arr = new ArrayList<>();
for (int i = 0; i < 5; i++) {
arr.add(new ArrayList<Optional<Integer>>());
}
Generics aside, you can't cast an Object[][] to a raw-typed Optional[][]. You'll get a ClassCastException at runtime. The array has to be created as an Optional[][], not as Object[][]. But generics are usually preferred raw types.
It's not that you can never create arrays of generics. You have to do so indirectly. Typically the way to do it is to create arrays of unbounded-wildcard generics, and then do an unchecked cast -- as you've done -- to the right type:
#SuppressWarnings("unchecked")
Optional<Integer>[][] arr = (Optional<Integer>[][]) new Optional<?>[5][5];
The above applies to the creation of any arrays of some specific generic type. In this case, you might consider using OptionalInt instead of Optional<Integer>. This bypasses any concerns about arrays of generics.
(Overall I'm somewhat suspicious of the notion of creating arrays or collections of Optionals of any flavor. It just seems like an odd thing to do. There are often better alternatives. But it might be justified in some cases. Anyway, whether an array of Optionals is appropriate for whatever problem you're trying to solve is a separate question.)

why do we need the pure <?> in Java?

why do we need the pure <?> in Java?
Does anything passed can only be used as Object ? So it's the same useful as any class casted to Object (only 9 methods could be available)
Thanks
I mean, if you have List< ? > list, you can only use items as Object. This means list.get(0) gives you Object and nothing more. Yes, also I can store to there anything, but is it useful for you? It's like you have a good an expensive thing and put it to a garbage bin. The only you can get from a trash bin is trash. As well you can put any good object of any class to List but get only Object from there with only 9 methods.
There are two scenarios where an unbounded wildcard is a useful approach:
If you are writing a method that can be implemented using functionality provided in the Object class.
When the code is using methods in the generic class that don't depend on the type parameter.For example, List.size, or List.clear. In fact, Class<?> is so often used because most of the methods in Class<T> don't depend on T.
For example, see Collections.swap method:
public static void swap(List<?> list, int i, int j) {
final List l = list;
l.set(i, l.set(j, l.get(i)));
}
Knowing the type does not aid in swapping two elements within a List, so an unbounded wildcard is used. You pass in a List - any List - and the method swaps the indexed elements. There is no type parameter to worry about.
For more information, see: Unbounded Wildcards.
I think the best answer is because sometimes you literally do not know what type a class is parameterized with. As Michael pointed out List<?> is not the same as List<Object>. So ? provides different semantics, which are useful in ensuring program correctness.
Consider two lists. One list is List<String> and the other is List<Integer>. Both of those are Objects, but we still don't want to get them mixed up. We don't want to put a String in the Integer list or vice-versa. So if you have a type declared as List<?>, you cannot safely put any type in that list. The ? could be String or Integer, but you don't know what it is, and you don't want to mix them up, so you can't put either in.
OTOH, List<Object> is just a list of objects, and since you declared it to hold any objects, you can safely put both String and Integer in the list. You said you didn't care what went into the list by declaring it of type Object, so any type is fine. In any meaningful way I can think of, List<Object> is equivalent to a raw type. It's a list that you manage yourself, without help from the compiler.

Does ArrayList<Integer> allow adding of String?

I came across the following code, a simple example of adding elements to List
List list = new ArrayList<Integer>();
ListIterator<Integer> litr = null;
list.add("A");
list.add("1");
list.add(5);
litr = list.listIterator();
while(litr.hasNext()){
System.out.println("UIterating " + litr.next());
}
I expected it to throw an ClassCastException, but rather it wrote this to the console
A
1
5
which looks weird. When i tried:
List<Integer> list = new ArrayList<Integer>();
I got a compile time error.
I would be grateful if someone could explain how the String objects are added to the ArrayList
You assigned the new ArrayList to an untyped List. Generic type restrictions don't apply to an untyped List, it will let you put whatever you want in it. The compiler does not keep track that your untyped List refers to something that was declared with a generic type.
In any case this wouldn't produce a ClassCastException, generics only affect compilation. At runtime
The case where you put the type on the list variable:
List<Integer> list = new ArrayList<Integer>();
is preferred, it should generate a compiler error telling you you're putting the wrong type in the collection.
There's a description of how legacy, non-generic code and generic code interoperate in this article:
In proper generic code, Collection would always be accompanied by a type parameter. When a generic type like Collection is used without a type parameter, it's called a raw type.
Most people's first instinct is that Collection really means Collection<Object>. However, as we saw earlier, it isn't safe to pass a Collection<Part> in a place where a Collection<Object> is required. It's more accurate to say that the type Collection denotes a collection of some unknown type, just like Collection<?>.
But wait, that can't be right either! Consider the call to getParts(), which returns a Collection. This is then assigned to k, which is a Collection<Part>. If the result of the call is a Collection<?>, the assignment would be an error.
In reality, the assignment is legal, but it generates an unchecked warning. The warning is needed, because the fact is that the compiler can't guarantee its correctness. We have no way of checking the legacy code in getAssembly() to ensure that indeed the collection being returned is a collection of Parts. The type used in the code is Collection, and one could legally insert all kinds of objects into such a collection.
So, shouldn't this be an error? Theoretically speaking, yes; but practically speaking, if generic code is going to call legacy code, this has to be allowed. It's up to you, the programmer, to satisfy yourself that in this case, the assignment is safe because the contract of getAssembly() says it returns a collection of Parts, even though the type signature doesn't show this.
This is possible because of how generics are implemented in Java - using type erasure, and because Java supports raw types for backward compatibility with old versions of Java (1.4 and older).
Generics only exist in your source code. The compiler uses them to check the types at compile-time, but then throws away the generics. At runtime, a List<Integer> is just a List of objects, and it doesn't know that it's a list that should contain only Integer objects.
Java supports the use of raw types such as List instead of List<Integer> for backward compatibility with old versions. When you use a raw type, as you are doing in your code above, you get a compiler warning. You should not use raw types in new code - only ever use them when you need to deal with old code that you can't change.
The combination of raw types and type erasure allows you to put types of objects in lists that you shouldn't be putting in there.
Because the List at runtime doesn't know anything about the type that its elements are supposed to have, it doesn't check anything so you will not get a ClassCastException.

Java covariant array bad?

I've been told by several people that Java allows covariant array subtyping in other words if A is a subtype of B, then A[] is a subtype of B[], but that this is a bad feature because it can lead to runtime errors. Can someone give me a concrete example to illustrate how it causes runtime errors and if/how does Java address this problem?
Very simple.
String strings[] = {"Broken","Type", "system"};
Object objects[] = strings;
objects[0] = 5; // compiles fine, but throws ArrayStoreException at runtime
Covariant types are not bad as long as you as you take things out, but the moment you put things in, the whole thing breaks.
Imagine you have a method takes an Object[] as a parameter.
fn(Object[]a){
...
}
wouldn't it be nice to be able to call it with a String[]?
String[] s = {"I","didn't","know","that","this","was","broken"}
fn(s);
Well, it sounds natural to be able to do so, especially in the early days when we didn't have generics in the language. And all this works fine as long as nothing get mutated, and Java doesn't provide any mechanism to guarantee that.
You should always favour Lists over arrays, because Lists use generics which are invariant.

Generic List of Array

How remove the:
Type safety: The expression of type
List[] needs unchecked conversion to conform to List<Object>[]
compiler warning in the following expression:
List<Object>[] arrayOfList = new List[10];
Afaik the only way is to use #SuppressWarnings("unchecked"). At least if you want to avoid the raw type warning that occurs in Matthew's answer.
But you should rethink your design. An Array of Lists? Is that really necessary? Why not a List of Lists? And if it gets too complicated (a List of List of Map of List to...), use custom data types. This really makes the code much more readable.
And as an unrelated side note: You should write List<Object>[] arrayOfList. The brackets are part of the type, not the variable.
You cannot do any variation of this without a compiler warning. Generics and arrays do not play nice. Though you can suppress it with
#SuppressWarnings("unchecked")
final List<Object> arrayOfList[] = new List[10];
List arrayOfList[] = new List[10];
or
List[] arrayOfList = new List[10];
You can't have generic arrays in Java, so there is no reason to have a reference to one. It would not be type-safe, hence the warning. Of course, you're using <Object>, which means your lists are intended to contain any object. But the inability to have generic arrays still applies.
Also, note that the second version above (with [] attached to the type) is usually considered better style in Java. They are semantically equivalent.
There are a number of ways:
You can add `#SuppressWarnings("unchecked") before the assignment to tell the compiler to ignore the warning
Use raw types List[] arrayOfList = new List[10]; notice that in Java you usually put the [] after the type not the variable when declaring it - though using raw types is discouraged by Sun since they might be removed in a future version.
Don't use arrays, it's usually a bad idea to mix collections with arrays: List<List<Object>> listOfList = new ArrayList<List<Object>>;
Unfortunately, due to the fact that generics are implemented in Java using type erasure, you cannot create an array of a type with type parameters:
// This will give you a compiler error
List<Object>[] arrayOfList = new ArrayList<Object>[10];
See this in Angelika Langer's Java Generics FAQ for a detailed explanation.
You could remove the generics and use raw types, as Matthew Flaschen shows, or use a collection class instead of an array:
List<List<Object>> data = new ArrayList<List<Object>>();

Categories