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.)
Related
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.
What's the difference between using a typed vs. non-typedArrayList in Java?
For example, Using an ArrayList of CustomObject:
Typed:
ArrayList<CustomObject> typedArray = new ArrayList<>();
typedArray.add(new CustomObject);
or non-typed:
ArrayList<> nonTypedArray = new ArrayList<>();
nonTypedArray.add(new CustomObject);
Is there any situation where the latter is preferred? Is there any difference when the ArrayList is holding different datatypes, e.g. an ArrayList of String, Int, etc.?
In the Second Approach, it is not mandatory to add only CustomObject whereas it is in 1st Approach, otherwise, you will get Compilation Error.
ArrayList<CustomObject> typedArray = new ArrayList<>();
typedArray.add(new CustomObject());
This approach is generally preferable as there are no chances of having Class Cast Exception but in second approach there are high chances of that !!
JavaDocs explains it beautifully : Why to prefer Generics
Stronger type checks at compile time.
Elimination of casts.
Enabling programmers to implement generic algorithms.
It's never preferable to use the latter option. I don't think that is even possible. I think you meant:
ArrayList nonTypedArray = new ArrayList();
This syntax is left over from Java 1.4 and earlier. It still compiles for the purposes of backwards compatibility.
Generics was introduced in Java 1.5 which allowed you to specify the types between angled brackets.
It is always preferable to use generics because it is more type-safe.
That is, if you specify
ArrayList<String> typedArray = new ArrayList<String>();
Then you cannot accidentally add an integer to this array list; if you tried to add an integer, the program would not compile.
Of course, Generics ensures type safety at compile time. At runtime ArrayList<String> typedArray = new ArrayList<String>(); becomes ArrayList typedArray = new ArrayList();. This is to maintain backwards compatibility.
What's the difference between using a typed vs. non-typed ArrayList in
Java?
A typed/generic ArrayList is a collection of objects in which the "type" of the object is defined in angled brackets. Generics were introduced in Java 5 to create type-safe collections.
Before Generics the collection was called untyped/raw type collection because there was no way to specify the compiler the type of the collection being created.
The difference between both is to detect type-safe operations at compile time.
In both of your cases, you are adding object(s) of type 'CustomObject' to the ArrayList. There will be no issue while adding elements in the list, as both lists will consider them as typed objects.
Typed:
ArrayList<CustomObject> typedArray = new ArrayList<CustomObject>();
typedArray.add(new CustomObject);
Untyped:
ArrayList<> nonTypedArray = new ArrayList<>();
nonTypedArray.add(new CustomObject);
Is there any situation where the latter is preferred?
I don't think so. As generics are recommended to be used while creating a list to ensure type-safe operations.
Is there any difference when the ArrayList is holding different
datatypes, e.g. an ArrayList of String, Int, etc.?
Surely, there is a reasonable difference. For an untyped list, you will need to add type-cast while fetching elements from a list. As there is a possibility of the compiler throwing a ClassCastException at runtime due to different types of elements.
In runtime, there is absolutely no difference, however in compilation time, using type parameters can save you from a plethora of errors, so it is always preferable to use generics properly.
The only case where raw types are used reasonably is in legacy applications, but even in this case, you try to use typed parameters if you can.
The use of type simplifies your coding removing the need of casting and also stores your data efficiently
https://docs.oracle.com/javase/tutorial/java/generics/why.html
Yeah, I know this is an old post. But I wanted to share an instance where an untyped ArrayList is useful: when you're writing a function that supposed to act on arbitrary element types. For example, suppose you want to make a generic shuffle function that knows how to shuffle an array. Like so:
ArrayList<Die> diceRolls = getGetPossibleDiceRolls();
ArrayList<Card> cardDeck = getPossibleCards();
ArrayList<GirlToDate> blackbook = getBlackbook();
shuffle(diceRolls);
shuffle(cardDeck);
shuffle(blackbook);
.
.
void shuffle(ArrayList array) {
int size = array.size();
for (int i=0; i<size; ++i) {
int r = random.nextInt(size - i) + i;
// Swap
Object t = array.get(i);
array.set(i, array.get(r));
array.set(r, t);
}
}
Some might argue "yeah, but the proper way to do this is to create an interface or subclass of something like a Shuffleable type..." But really?
In Java 1.7 and upwards you should normally use the constructor like this:
ArrayList<MyObject> list = new ArrayList<>();
or else for a more general List object:
List<MyObject> list = new ArrayList<>();
Observe that you only specify the type <MyObject> once, not twice. This makes your code easier to maintain. The <> causes the constructor to return an ArrayList which is already typed to match the field/variable to which it is being assigned - so that no cast will be required in the calling code.
Do not use new ArrayList() as the constructor. This returns an untyped ArrayList which then has to be cast to a type to match the field/variable to which it is being assigned. This means unnecessary type checking and casting and so generally reduces performance.
Is there no any short cut to do this nicely?
That ugly two loops (One loop to read the pmList and second loop to add to the markupArray) is the only option (Instead of ArrayUtils).
ArrayList<Double> pmList = new ArrayList<Double>();
pmList.add(0.1); // adding through a loop in real time.
pmList.add(0.1);
pmList.add(0.1);
pmList.add(0.1);
double[] markupArray = new double[pmList.size()];
arkupArray = pmList.toArray(markupArray); // This says The method toArray(T[]) in the type ArrayList<Double> is not applicable for the arguments (double[])
Simply use a Double[] array, instead of double[] then everything works fine. If you know the size of the list ahead of time, you can also skip the list and insert directly into the array. It might even be worth to traverse the input two times: Once for retrieving its size and once for insertion.
Auto boxing only works for primitive types, not for arrays of primitive types. A double[] array is no T[] array, since a type parameter T must always be an Object. While a double may be autoboxed to T (with T=Double), a double[] cannot be autoboxed to T[].
The reason why arrays are not autoboxed is probably that this operation would be very costly: Boxing an array means creating a new array and boxing each element. For large arrays, this has a huge performance hit. You don't want such a costly operation to be done implicitly, hence no autoboxing for arrays. In addition, boxing a complete array would yield a new array. Thus, when writing to the new array, the changes would not write through to the old array. So you see, there are some semantics problems with array-boxing, so it is not supported.
If you must return a double[] array, then your must either write your own function or use a third-party library like Guava (see msandiford's answer). The Java Collections framework has no methods for (un)boxing of arrays.
You could use TDoubleArraList or guava's primitive list collection.
You could also determine the size in advance in one loop and add the values in another.
Why not make your own shortcut?
static double[] doubleListToArray(List<Double> list) {
int k = 0;
double[] result = new double[list.size()];
for(double value : list)
result[k++] = value;
return result;
}
Google guava has Doubles#asList(...) and Doubles#toArray(...) which provide conversions from double[] to List<Double> and from Collection<? extends Number> to double[] respectively.
You are right that this is not very intuitive at first look. However, this limitation is related to the way the Java language implements generic types and auto-boxing:
Generic types are erased at runtime. This implies that any ArrayList<Double> is represented by a single compiled Java class ArrayList which is shared with other generic representations of ArrayList such as for example ArrayList<String>. As a consequence, the compiled method ArrayList::toArray does not (and must not) know what generic type an instance represents as the single compiled method must be applicable for any generic type. As the elements could therefore be anything like String or Double, you need to provide an array to the method. The method can then check the type of the target array at runtime and check the elements that are filled into the array at runtime to be assignable to the array's component type. All this logic can be implemented by a single compiled method.
Secondly, auto-boxing and -unboxing is something that only exists at compile time. This means that the statements
Integer i = 42;
int j = i;
are compiled as if you wrote
Integer i = new Integer(42);
int j = i.intValue();
It is the Java compiler that adds the boxing instructions for you. The Java runtime applies a slightly different type system where boxing is not considered. As a consequence, the single compiled method ArrayList::toArray that we mentioned in (1) cannot know that this boxing needs to be applied as we argued that the method must be applicable for any type T which might not always represent a Double.
In theory, you could alter the implementation of ArrayList::toArray to explicitly checks if an array's component type and a lists element type are applicable for unboxing but this approach would result in several branches which would add quite a runtime overhead to the method. Rather, write a small utility method that specializes on the Double type and applies the implicit unboxing due to the specialization. An iteration over all list items suffices for this purpose, this is how the ArrayList::toArray is implemented as well. If your array is small, consider to use an array of boxed values Double[] instead of double[]. If your array is however large, lives long or you are restrained to primitive types in order to comply to a third-party API, use the utility. Also, look out for implementations of primitive collections if you want to ungo the overall boxing. With Java 8, use a Stream in order to inline the array conversion.
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.
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>>();