why does Java allow generic array declaration? - java

I know that array generic array creation is not allowed because arrays need to know their type at run-time but since generic erase their type information at run-time it is not possible to create generic array.
But how come it allows generic array declaration as follow :
private E[] genericArray;// this line does not prevent the class from compiling
private E[] genericArrayTwo= new E[][10];// this line prevents the class from compiling

private E[] genericArray;// this line does not prevent the class from compiling
private E[] genericArrayTwo= new E[][10];// this line prevents the class from compiling
Your first example was compile time evaluation to ensure proper
typing. Simply says that this array may contain something of type E.
Your second example would need to be executed at run time when E has
already been erased. Can't create an array of type E because E is no longer available.
Allowing generic array declarations ensures that appropriate types are matched at compile time.
Integer[] ints1 = null;
String[] str1 = null;
// both requires cast or it won't compile
Integer[] ints = (Integer[])doSomething1(ints1);
String[] str = (String[])doSomething1(str1);
//but that could result in a runtime error if miscast.
//That type of error might not appear for a long time
// Generic method caters to all array types.
// no casting required.
ints = doSomething2(ints1);
str = doSomething2(str1);
}
public static Object[] doSomething1(Object[] array) {
return array;
}
public static <T> T[] doSomething2(T[] array) {
return array;
}
It allows examples such as the following:
public <T> void copy(List<T> list, T[] array) {
for (T e : array) {
list.add(e);
}
}
You could then assign a value from either the list or the array to some variable of type T without getting a class cast exception or without having to to an instanceof test.

If E is a formal generic of the current class, yes you can do that :
List<E> e = new ArrayList<E>();
but you cannot do that :
E[] e = new E[10];
But declaring the E[] e variable makes sense all the same.
because no one prevents you from valuing the array from the client side that knows the real type of the array :
Foo<E> class{
private E[] array;
Foo(E[] array) {
this.array = array;
}
}
And to use it as :
Foo<String> foo = new Foo<>(new String[] { "a", "b" });
Or as alternative you could also pass the class of the array to instantiate from the client side :
Foo<String> foo = new Foo<>(String.class);
So you see that declaring E[] array is not so helpless.

Related

How to write a generic function which accepts arrays of literals

I am unable to provide values of type int[], float[], etc. to a generic function. I get errors that say basically that float[] is the wrong type and Float[] is what the function actually takes.
Here's an example of a method I wrote, and I'm trying to give it values like new int[]{0,1} (created in library somewhere else).
private static <T> JSONArray encodeArray(T[] array) {
JSONArray arr = new JSONArray();
Collections.addAll(arr, array);
return arr;
}
Is it even possible to write my function signature to accept these arrays of literals?
I could go to the call site, and do a conversion of float[] to Float[], but I don't know how to do that either.
Is it even possible to write my function signature to accept these
arrays of literals?
It is possible, but the parameter type will have to be Object, because that is the only common superclass of "arrays of primitives" and "arrays of references" (e.g. it can't be Object[] since arrays of primitives are not subclasses of Object[]). (There are also some interfaces that all arrays implement, but I will ignore those for now.) Unfortunately, this means that you will lose type safety as the compiler will not be able to give an error at compile time if someone passes a non-array type in.
To do array operations on this Object value, you will need to use the methods in the reflection helper class java.lang.reflect.Array. So you can do something like this:
import java.lang.reflect.Array;
// ...
private static JSONArray encodeArray(Object array) {
JSONArray arr = new JSONArray();
for (int i = 0, n = Array.getLength(array); i < n; i++) {
arr.add(Array.get(array, i)); // primitives are automatically wrapped
}
return arr;
}
The method which accepts a generic array.
public <T> void printArray(T[] array){
for (T element: array){
System.out.println(element);
}
}
You can't use primitives in generic functions. When generic are compiled, you end up with Object[] in the above example as the implementing type. As int[] and byte[] etc, do not extend Object[] you cannot use them interchangeably even if the code involved would be identical (again generics are not templates)
Add on the solution from #Max-Reshetnyk, It is better if you check ArrayList for methods that help you add or remove... elements. Since primitive types are not meant to be used with generics, you should AutoBox them with their respective types and then use generics.
For instance:
class Main {
public static void main(String[] args) {
Integer[] x = new Integer[1];
x[0] = 1;
printArray(x);
}
public static <T> void printArray(T[] array){
for (T element: array){
System.out.println(element);
}
}
}

Strange compilation error when Java Generic T is parametrized by different parameter types

I was going through this link provided by Java Oracle regarding generics. Now under section Generic Methods, it explains that Generic T can take different parameters at same time.
Example Code
public static void main(String[] args)
{
Object [] arrObjects = new Object[1];
List<String> stringList = new ArrayList<>();
List<Object> objectList = new ArrayList<>();
String [] arrString = new String[1];
fromArrayToCollection(arrString, objectList); // Works properly
fromArrayToCollection(arrObjects, stringList); // Gives compilation error
}
private static <T> void fromArrayToCollection(T[] a, Collection<T> c) {
for (T o : c) {
a[0] = o; // Correct
}
}
Here in the example fromArrayToCollection method call is right in one case, but throws compilation error in other case.
Also the link also says similar line
We can call this method with any kind of collection whose element type is a supertype of the element type of the array.
It says collection element type should be supertype of array element but why not otherwise.

<T> T[] toArray(T array[])

Can somebody explain in Java Collection Framework what the importance is of <T> in the signature
<T> T[] toArray(T array[]).
I know that <T> represents generic. But an elaborate explanation based on an example will be a great deal of help.
This method allows you to create array of required type. The default toArray() returns Object[] which is cumbersome if you have list of Strings and you want a String[] from it.
For example:
List<String> list = new ArrayList<>();
list.add("A");
String[] listToArray;
listToArray = list.toArray(); // This won't compile as to Array gives Object[]
listToArray = list.toArray(new String[list.size()]); // This works when I want String[]
The <T> is the way Java says "in the method declaration that follows, T is a generic parameter rather than a specific class with the name T.
For example:
class T {
public int x;
}
<T> T f(T x) { return x; }
T g(T x) { return new T(); }
The method f can be used with arguments of any type (such as String), whereas g can only be passed arguments of the class T defined above. That is, one can call
f("dog")
but NOT g("dog") because a string is not a T.
Runnable example at ideone showing how f and g are used.

Initialize an array of a generic type in the constructor of a class without having to pass the Class<T> parameter

I have the class:
class SomeClass<T extends SomeInterface>{
private T[] myArray;
public SomeClass()
{
// I want to initialize myArray in here to a default size of 100
myArray = new T[100]; // this gives an error
}
}
I know I can fix that by requiring a parameter in the constructor as:
class SomeClass<T extends SomeInterface>{
private T[] myArray;
public SomeClass(Class<T> clazz)
{
myArray= (T[]) Array.newInstance(clazz, 100);
}
}
but it makes no scene having to pass the generic parameter twice.
in other words in order to instantiate an object from the class SomeClass I will have to do something like:
SomeClass<SomeOtherClass> obj =
new SomeClass<SomeOtherClass>(SomeOtherClass.class);
I program in c# and Java does not seem to be friendly. I don't even understand why it is not possible to cast Object[] array to SomeOtherClass[] array. In c# that will be possible...
so my question is how can I avoid having to pass the SomeOtherClass.class parameter in order to be able to construct an array of the generic type in the constructor of the class...
While Shakedown listed the fix to your problem, allow me to explain why it is not typesafe to create a generic array.
I will illustrate why with an example from Effective Java 2nd Ed.
// Why generic array creation is illegal - won't compile!
List<String>[] stringLists = new List<String>[1]; // (1)
List<Integer> intList = Arrays.asList(42); // (2)
Object[] objects = stringLists; // (3)
objects[0] = intList; // (4)
String s = stringLists[0].get(0); // (5)
Let’s pretend that line 1, which creates a generic array, is legal.
Line 2 creates and initializes a List<Integer> containing a single
element.
Line 3 stores the List<String> array into an Object array
variable, which is legal because arrays are covariant.
Line 4 stores the List<Integer> into the sole element of the Object array, which
succeeds because generics are implemented by erasure: the runtime type
of a List<Integer> instance is simply List, and the runtime type of a
List<String>[] instance is List[], so this assignment doesn’t generate
an ArrayStoreException. Now we’re in trouble. We’ve stored a
List<Integer> instance into an array that is declared to hold only
List<String> instances.
In line 5, we retrieve the sole element from
the sole list in this array. The compiler automatically casts the
retrieved element to String, but it’s an Integer, so we get a
ClassCastException at runtime. In order to prevent this from
happening, line 1 (which creates a generic array) generates a
compile-time error.
Yes, you would have to pass in the .class like that in order to make this work.
You could avoid all of this and just use an ArrayList<T> instead. When you need it in the form of an array you can use: (T[]) myArrayList.toArray()
All and every type in java is a subclass of Object class so this can be achieved using this.
public StackArray(int size){
dataStack = (T[])new Object[size];
}

Generics: Why cannot I have Object as parameter for my Collection?

I am going through Generics Tutorial and was going through example to copy objects from array to collection.
Code
static void fromArrayToCollection(Object[] a, Collection<?> c) {
for (Object o : a) {
c.add(o); // Compile time error
}
}
I am thinking that I can pass object as parameter to collection and it should work without any issues but tutorial says
By now, you will have learned to avoid the beginner's mistake of trying to use Collection as the type of the collection parameter.
Why does it say that passing Object as parameter type to Collection is not correct approach?
Updated:
static void fromArrayToCollection(Object[] a, Collection<Object> c) {
for (Object o : a) {
c.add(o); // Compile time error
}
}
The "beginner mistake" they're referring to is saying Collection<Object> when what you were trying to say is "any Collection/Collection of Anything." It would in the abstract be perfectly legal to declare the method as Collection<Object> it just doesn't meet the design goal of a method that takes in anything.
We want to be able to do this:
public static void main(String[] args) {
String[] stringArray = {"A", "B", "C"};
List<String> stringList = new ArrayList<String>();
fromArrayToCollection(stringArray, stringList);
Integer[] intArray = {1, 2, 3};
List<Integer> intList = new ArrayList<Integer>();
fromArrayToCollection(intArray, intList);
}
public static <T> void fromArrayToCollection(T[] array, Collection<T> collection) {
for (T item: array) {
collection.add(item);
}
}
That wouldn't work if you made it Collection<Object>.
You can't declare the parameter type as Collection<Object> and have it work for multiple types like above because generic types aren't covariant. It would be illegal to say, pass in List<String> to a method with an argument type of Collection<Object>. A Collection<String> is not a Collection<Object>.
Consider the standard example:
List<Integer> intList = new ArrayList<Integer>();
List<Object> objList = intList; //doom impending!!!
objList.add("NOTANUMBER");
int i = intList.get(0).intValue(); //runtime exception!
That's why it's illegal to declare it as Collection<Object> and take a collection of anything.
Your code is equivalent to the following:
static void <T> fromArrayToCollection(Object[] a, Collection<T> c) {
for (Object o : a) {
c.add(o); // Compile time error
}
}
It won't work because there is no restriction on the type of T. For example, you could set T to be Integer, and you are saying that you will add in any instance of Object into a Collection of Integer.
Hope this helps.
What you want is this:
static <T> void fromArrayToCollection(T[] a, Collection<T> c) {
for (T o : a) {
c.add(o); // No compiler error
}
}
You can type the array too. By declaring a typed parameter for the method, you can give that type to each of your inputs.
You could also do this:
static void fromArrayToCollection(Object[] a, Collection<? super Object> c) {
for (Object o : a) {
c.add(o); // No compiler error
}
}
But this seems a little trite.
Following the explanation here, it tells you that you should not rely that a Collection of Something is actually a Collection of Objects, thus they tell you that you should use a Collection of unknow, but you cannot add anything to that collection, you could corrupt it; imagine the following:
static void fromArrayToCollection(Object[] a, Collection<?> c) {
for (Object o : a) {
c.add(o); // Imagine that this will compile
}
}
// somewhere else in the same class
Collection<String> myStrings = new ArrayList<String>();
fromArrayToCollection(someListOfObjects, myStrings);
myStrings.get(0).doSomethingOnlyStringsDo(); // surprise, surprise, i am not a string.

Categories

Resources