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

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.

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);
}
}
}

Autoboxing and type detection in Java generics

I have following code:
List<Integer> ints = Arrays.asList(1,2,3,4);
asList, defined in java.util.Arrays is as:
public static <T> List<T> asList(T... a) {
return new ArrayList<>(a);
}
And AaaryList constructor is defined as:
ArrayList(E[] array) {
a = Objects.requireNonNull(array);
}
I was expecting to find some code for autoboxing,and some code which determines type of the elements passed to asList method as we can pass any type to asList, but no such code is there.
So how does all this type detection, autoboxing etc occurs in this entire scheme of things.

why does Java allow generic array declaration?

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.

Overload method with generic method and parameter

I've been looking through countless pages trying to figure out if it is possible to overload a method with a List parameter, with another method that has the generic return parameter List<E>. I understand that this should always try to be avoided, but it's for an assignment, and I would like to keep both methods included if it is actually possible. Below is the code for the two methods:
// This method returns an array of the info.
Object[] toArray(List data) {
Object[] array = new Object[data.size()];
for (int i = 0; i < data.size(); i++) {
array[i] = data.get(i);
}
return array;
}
// Overloaded version that uses Generics.
E[] toArray(List<E> data) {
return toArray(data);
}
My question is, besides changing the name, is there any way to differentiate the parameters that would allow both methods to be in place, and that would not cause a name clash error? Any help is appreciated. Thank you in advance!
Note: someone asked the question "How to overload a method with generic parameter in java?" on StackOverflow, which was close to what I needed, but not quite.
You don't need to overload the method because Java does this implicitly for you with raw types.
<E> E[] toArray(List<E> data) {
If you call
List objs = ...
Object[] array = toArray(objs);
and if you use generics you get generics
List<String> strs =
String[] array = toArray(objs);
The real problem you have is there is no way to implement toArray. This is because the generic type is not known at at runtime. i.e. you can't create an array of E nor can you cast an Object[] to a String[] for example.
What you can do is pass the type of the array to use.
public static Object[] toArray(List list) {
return toArray(list, Object.class);
}
public static <E> E[] toArray(List<? extends E> list, Class<E> eClass) {
#SuppressWarnings("unchecked")
E[] array = (E[]) Array.newInstance(eClass, list.size());
for (int i = 0; i < array.length;i++)
array[i] = list.get(i);
return array;
}

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