I am trying to filter methods which has primitive arrays as their arguments.
One of the method signature is as follows:
public void myMeth(int[]);
public void myMeth(double[]);
Executing the following program,
public class MyClass {
public static void main(String args[]) {
System.out.println(int[].class);
System.out.println(int[].class.isPrimitive());
System.out.println(Integer[].class);
System.out.println(int.class);
System.out.println(int.class.isPrimitive());
}
}
I get the following output:
class [I
false
class [Ljava.lang.Integer;
int
true
Now my questions are:
Since int is primitive type, why is not int[] primitive?
How to know if a type is a primitive array?
Since int is primitive type, why is not int[] primitive?
Any array is an object in Java because it can be created with new operator as follows:
int[] a = new int[5];
Also, array is a sub-class of Object, therefore you can call all Object class' methods on the array object.
int[] a = new int[5];
int x = 10;
System.out.println(a.toString());
System.out.println(x.toString()); // compile-time error
From the Java Specification: Section #4.3.1
An object is a class instance or an array.
So, the condition (type.isArray() && type.isPrimitive()) is always false because array is not a primitive type.
How to know if a type is a primitive array?
You need to make use of the getComponentType() method. From Javadocs
Returns the Class representing the component type of an array. If this
class does not represent an array class this method returns null.
So, the code snippet would be:
public static boolean isPrimitiveArray(Class<?> type) {
Class<?> componentType = type;
while ((componentType = componentType.getComponentType()) != null) {
if (componentType.isPrimitive()) {
return true;
}
}
return false;
}
System.out.println(isPrimitiveArray(int[].class)); // true
System.out.println(isPrimitiveArray(int[][].class)); // true
System.out.println(isPrimitiveArray(int[][][].class)); // true
System.out.println(isPrimitiveArray(Integer.class)); // false
System.out.println(isPrimitiveArray(Integer[].class)); // false
System.out.println(isPrimitiveArray(int.class)); // false
or if we want to check dimensions also..
public static boolean isPrimitiveArray(Class<?> type, int dimensions) {
Class<?> componentType = type;
int count = 0;
while ((componentType = componentType.getComponentType()) != null) {
count++;
if (componentType.isPrimitive() && dimensions == count) {
return true;
}
}
return false;
}
System.out.println(isPrimitiveArray(int[].class,1)); // true
System.out.println(isPrimitiveArray(int[][].class,2)); // true
System.out.println(isPrimitiveArray(int[][][].class,2)); // false
I have below generic method that returns a generic array:
public static <T> T[] genericMethod1(List<T> input) {
T[] res = (T[]) new Object[input.size()];
int i = 0;
for (T t : input) {
res[i] = t;
i++;
}
return res;
}
public static <T> T genericMethod2(List<T> input) {
return input.get(0);
}
But later when I try to get the result array with:
LinkedList<Integer> list = new LinkedList<Integer>();
list.addFirst(1);
list.addFirst(1);
Integer[] i = (Integer[]) genericMethod1(list); // 1) Runtime error
Integer j = genericMethod2(list); // 2) works
For case 1, I always get error at runtime:
Exception in thread "main" java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to [Ljava.lang.Integer;
Anybody can explain why and how to return the generic array properly? Thanks.
Below is my understanding, please correct me if I'm wrong.
As Tim mentioned, type erasure happened at compile time, so in bytecode, each T object is just type Object, meanwhile, compiler will add type cast from Object to T "properly".
Say T is an Integer, where T is declared, it's Object. For where it's referred, it's type cast (implicitly) to T.
EXCEPT that if T[] array is declared, it's Object[], and where the array is referred, it stays Object[]. No implicit cast to T[] happens.
The explanation for what you are seeing is due to something called type erasure. Here is what your genericMethod() will look like after the compiler performs type erasure:
public static Object[] genericMethod(List input) {
Object[] res = new Object[input.size()];
int i = 0;
for (Object t : input) {
res[i] = t;
i++;
}
return res;
}
In other words, this method will return an array of type Object. There is no way to cast an Object[] to an Integer[] because they are not the same type. If you want your method to be able to dynamically return the type you want, then you can use Array.newInstance(). This will require also passing in the type of the array you want as an input parameter:
public static <T> T[] genericMethod(Class<T> clazz, List<T> input) {
#SuppressWarnings("unchecked")
T[] res = (T[]) Array.newInstance(clazz, input.size());
int i = 0;
for (T t : input) {
res[i] = t;
i++;
}
return res;
}
Now your code snippet will run without error:
LinkedList<Integer> list = new LinkedList<Integer>();
Integer[] i = genericMethod(Integer.class, list);
Update:
Your second method, genericMethod2(), will look like this after type erasure:
public static Object genericMethod2(List input) {
return input.get(0);
}
It will return the first element of the input list, cast to Object. Here is your usage of that method:
Integer j = genericMethod2(list);
The compiler will try to cast the output from genericMethod2() to Integer:
Integer j = (Integer)genericMethod2(list);
This cast is legal, because every Integer is also an Object, and furthermore it succeeds here because you passed in a collection of Integer. This second method is not the same scenario as the first one you highlighted for us.
When calling the method, genericMethod you are assuming that it returns array of integers, which is NOT correct. It actually returns array of type Object at runtime.
List<Integer> input = new ArrayList<Integer>();
input.add(1);
Object[] output = genericMethod(input);
for(Object obj : output){
System.out.println("Value= "+ (Integer)obj);
}
So we need to cast the individual content of the array.
One general guidline is that we shouldn't mix ARRAY and GENERICS in Java.
Update:
Reference from Effective Java:
In Summary, arrays and generics have very different type rules. Arrays are covariant and reified; generics are invariant and erased. As a consequcne, arrays provide runtime type safety but not compile-time type safety and vice versa for generics. Generally speaking, arrays and generics don’t mix well. If you find yourself mixing them and getting compile-time error or warnings, your first impulse should be to replace the arrays with lists.
Another way is to do it like in java.util.ArrayList.toArray(T[]).
You pass the type of Array to that Method, if it's big enough it will be reused, otherwise an new Array is generated.
Example:
List<Integer> intList = new ArrayList<>();
intList.add(Integer.valueOf(1));
intList.add(Integer.valueOf(2));
intList.add(Integer.valueOf(3));
Integer[] array = intList.toArray(new Integer[] {});
System.out.println(Arrays.toString(array));//Will Print [1, 2, 3]
Implementation of ArrayList.toArray(T[]) see here.
After Java 8 was released, you can leverage constructor references to return a generic array. While there are more straightforward options to convert List to Integer[], I am explaining the concept here with kinda minimal changes to your code:
I am not touching your genericMethod2() implementation
For genericMethod1, let's add a second parameter that will accept a constructor reference. Then you can call the apply() function on the constructor reference to create a generic array.
you pass Integer[]::new to this new parameter. Integer[]::new is treated as (int x) => new Integer[x]
fi.apply(input.size()) calls (int x) => new Integer[x] with argument input.size(). The result is a Integer[]. This is why the IntFunction parameter has generic type T[]
import java.util.List;
import java.util.function.IntFunction;
import java.util.LinkedList;
public class RetGenericArr {
public static <T> T[] genericMethod1(List<T> input, IntFunction<T[]> fi) {
T[] res = fi.apply(input.size());
int i = 0;
for (T t : input) {
res[i] = t;
i++;
}
return res;
}
public static <T> T genericMethod2(List<T> input) {
return input.get(0);
}
public static void main(String[] args) {
LinkedList<Integer> list = new LinkedList<Integer>();
list.addFirst(1);
list.addFirst(2);
// umm I am uncomfortable about the variable naming below:
Integer[] i = genericMethod1(list, Integer[]::new); // Now no error/warning. Note that I am not casting now
for (int e: i) {
System.out.println(e);
}
Integer j = genericMethod2(list);
}
}
I have searched for this, but unfortunately, I don't get the correct answer.
class Helper {
public static <T> T[] toArray(List<T> list) {
T[] array = (T[]) new Object[list.size()];
for (int i = 0; i < list.size(); i++) {
array[i] = list.get(i);
}
return array;
}
}
Test it:
public static void main(String[] args) {
List<String> list = new ArrayList<String>();
list.add("abc");
String[] array = toArray(list);
System.out.println(array);
}
But there is an error thrown:
Exception in thread "main" java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to [Ljava.lang.String;
at test.Helper.main(Helper.java:30)
How to solve this?
UPDATE
I want this method, because sometimes, the type in my code is too long:
newEntries.toArray(new IClasspathEntry[0])
I'd hope to call:
toArray(newEntries)
FINALLY
It seems impossible to create such a method, thank you all very much!
This is due to type erasure. The generics are removed in compilation, thus the Helper.toArray will be compiled into returning an Object[].
For this particular case, I suggest you use List.toArray(T[]).
String[] array = list.toArray(new String[list.size()]);
You can just call list.toArray(T[] array) and not have to worry about implementing it yourself, but as aioobe said, you can't create an array of a generic type due to type erasure. If you need that type back, you need to create a typed instance yourself and pass it in.
If you want to produce your method through brute force, and you can guarantee that you'll only call the method with certain restrictions, you can use reflection:
public static <T> T[] toArray(List<T> list) {
T[] toR = (T[]) java.lang.reflect.Array.newInstance(list.get(0)
.getClass(), list.size());
for (int i = 0; i < list.size(); i++) {
toR[i] = list.get(i);
}
return toR;
}
This approach has problems. As list can store subtypes of T, treating the first element of the list as the representative type will produce a casting exception if your first element is a subtype. This means that T can't be an interface. Also, if your list is empty, you'll get an index out of bounds exception.
This should only be used if you only plan to call the method where the first element of the list matches the Generic type of the list. Using the provided toArray method is much more robust, as the argument provided tells what type of array you want returned.
You can't instantiate a Generic type like you did here:
T[] array = (T[]) new Object[list.size()];
As, if T is bounded to a type, you're typecasting the new Object array to a bounded type T. I would suggest using List.toArray(T[]) method instead.
See Guava's Iterables.toArray(list, class).
Example:
#Test
public void arrayTest() {
List<String> source = Arrays.asList("foo", "bar");
String[] target = Iterables.toArray(source, String.class);
}
public static <T> T[] toArray(Collection<T> c, T[] a) {
return c.size()>a.length ?
c.toArray((T[])Array.newInstance(a.getClass().getComponentType(), c.size())) :
c.toArray(a);
}
/** The collection CAN be empty */
public static <T> T[] toArray(Collection<T> c, Class klass) {
return toArray(c, (T[])Array.newInstance(klass, c.size()));
}
/** The collection CANNOT be empty! */
public static <T> T[] toArray(Collection<T> c) {
return toArray(c, c.iterator().next().getClass());
}
String[] array = list.toArray(new String[0]);
The problem is the component type of the array that is not String.
Also, it would be better to not provide an empty array such as new IClasspathEntry[0].
I think it is better to gives an array with the correct length (otherwise a new one will be created by List#toArray which is a waste of performance).
Because of type erasure, a solution is to give the component type of the array.
Example:
public static <C, T extends C> C[] toArray(Class<C> componentType, List<T> list) {
#SuppressWarnings("unchecked")
C[] array = (C[])Array.newInstance(componentType, list.size());
return list.toArray(array);
}
The type C in this implementation is to allow creation of an array with a component type that is a super type of the list element types.
Usage:
public static void main(String[] args) {
List<String> list = new ArrayList<String>();
list.add("abc");
// String[] array = list.toArray(new String[list.size()]); // Usual version
String[] array = toArray(String.class, list); // Short version
System.out.println(array);
CharSequence[] seqArray = toArray(CharSequence.class, list);
System.out.println(seqArray);
Integer[] seqArray = toArray(Integer.class, list); // DO NOT COMPILE, NICE !
}
Waiting for reified generics..
As pointed earlier this will work:
String[] array = list.toArray(new String[0]);
And this will also work:
String[] array = list.toArray(new String[list.size()]);
However, in the first case a new array will be generated. You can see how this is implemented in Android:
#Override public <T> T[] toArray(T[] contents) {
int s = size;
if (contents.length < s) {
#SuppressWarnings("unchecked") T[] newArray
= (T[]) Array.newInstance(contents.getClass().getComponentType(), s);
contents = newArray;
}
System.arraycopy(this.array, 0, contents, 0, s);
if (contents.length > s) {
contents[s] = null;
}
return contents;
}
Worked solution!
Just copy interface and class inside your project.
This :
public interface LayerDataTransformer<F, T> {
T transform(F from);
Collection<T> transform(Collection<F> from);
T[] toArray(Collection<F> from);
}
and this :
public abstract class BaseDataLayerTransformer<F, T> implements LayerDataTransformer<F, T> {
#Override
public List<T> transform(Collection<F> from) {
List<T> transformed = new ArrayList<>(from.size());
for (F fromObject : from) {
transformed.add(transform(fromObject));
}
return transformed;
}
#Override
public T[] toArray(Collection<F> from) {
Class<T> clazz = (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[1];
T[] transformedArray = (T[]) java.lang.reflect.Array.newInstance(clazz, from.size());
int index = 0;
for (F fromObject : from) {
transformedArray[index] = transform(fromObject);
index++;
}
return transformedArray;
}
}
Usage.
Declare a subclass of BaseDataLayerTransformer
public class FileToStringTransformer extends BaseDataLayerTransformer<File,String> {
#Override
public String transform(File file) {
return file.getAbsolutePath();
}
}
And use :
FileToStringTransformer transformer = new FileToStringTransformer();
List<File> files = getFilesStub();// returns List<File>
//profit!
String[] filePathArray = transformer.toArray(files);
I use this simply function.
IntelliJ just hates that type cast T[] but it works just fine.
public static <T> T[] fromCollection(Class<T> c, Collection<T> collection) {
return collection.toArray((T[])java.lang.reflect.Array.newInstance(c, collection.size()));
}
And call looks like this:
Collection<Integer> col = new ArrayList(Arrays.asList(1,2,3,4));
fromCollection(Integer.class, col);
This gist that I wrote gives a good solution to this problem.
Following siegi's suggestion on Atreys' answer, I wrote a constructor which finds the "nearest common ancestor" (NCA) class and uses that class to create the array. If checks for nulls and if the provided Collection is length 0 or all nulls, the default type is Object. It totally ignores Interfaces.
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.ArrayList;
import java.lang.reflect.Array;
import java.util.Iterator;
public class FDatum<T> {
public T[] coordinates;
// magic number is initial size -- assume <= 5 different classes in coordinates
public transient HashSet<Class> classes = new HashSet<Class>(5);
public FDatum (Collection<T> coordinates) {
// to convert a generic collection to a (sort of) generic array,
// we need to bend the rules:
// 1. default class T is Object
// 2. loop over elements in Collection, recording each unique class:
// a. if Collection has length 0, or
// if all elements are null, class T is Object
// b. otherwise, find most specific common superclass, which is T
// record all unique classes in coordinates
for (T t : coordinates) this.classes.add(t.getClass());
// convert to list so we can easily compare elements
List<Class> classes = new ArrayList<Class>(this.classes);
// nearest common ancestor class (Object by default)
Class NCA = Object.class;
// set NCA to class of first non-null object (if it exists)
for (int ii = 0; ii < classes.size(); ++ii) {
Class c = classes.get(ii);
if (c == null) continue;
NCA = c; break;
}
// if NCA is not Object, find more specific subclass of Object
if (!NCA.equals(Object.class)) {
for (int ii = 0; ii < classes.size(); ++ii) {
Class c = classes.get(ii);
if (c == null) continue;
// print types of all elements for debugging
System.out.println(c);
// if NCA is not assignable from c,
// it means that c is not a subclass of NCA
// if that is the case, we need to "bump up" NCA
// until it *is* a superclass of c
while (!NCA.isAssignableFrom(c))
NCA = NCA.getSuperclass();
}
}
// nearest common ancestor class
System.out.println("NCA: " + NCA);
// create generic array with class == NCA
T[] coords = (T[]) Array.newInstance(NCA, coordinates.size());
// convert coordinates to an array so we can loop over them
ArrayList<T> coordslist = new ArrayList<T>(coordinates);
// assign, and we're done!
for (int ii = 0; ii < coordslist.size(); ++ii)
coords[ii] = coordslist.get(ii);
// that's it!
this.coordinates = coords;
}
public FDatum (T[] coordinates) {
this.coordinates = coordinates;
}
}
Here are some examples of using it in jshell ("unchecked" class warnings removed for brevity):
jshell> FDatum d = new FDatum(new ArrayList(Arrays.asList((double)1, (Double)3.3)))
class java.lang.Double
NCA: class java.lang.Double
d ==> com.nibrt.fractal.FDatum#9660f4e
jshell> d.coordinates
$12 ==> Double[2] { 1.0, 3.3 }
jshell> d = new FDatum(new ArrayList(Arrays.asList((double)1, (Double)3.3, (byte)7)))
class java.lang.Byte
class java.lang.Double
NCA: class java.lang.Number
d ==> com.nibrt.fractal.FDatum#6c49835d
jshell> d.coordinates
$14 ==> Number[3] { 1.0, 3.3, 7 }
jshell> d = new FDatum(new ArrayList(Arrays.asList((double)1, (Double)3.3, (byte)7, "foo")))
class java.lang.Byte
class java.lang.Double
class java.lang.String
NCA: class java.lang.Object
d ==> com.nibrt.fractal.FDatum#67205a84
jshell> d.coordinates
$16 ==> Object[4] { 1.0, 3.3, 7, "foo" }
When you have a generic List<T> you will be able to know the class of the object at the runtime. Therefore, the best way to implement it is like this:
public static <T> T[] list2Array(Class<T[]> clazz, List<T> elements)
{
T[] array = clazz.cast(Array.newInstance(clazz.getComponentType(), elements.size()));
return elements.toArray(array);
}
Why do you need the Class<T[]> parameter?
Because, we have a generic list and it will not provide the information necessary to get an array of precisely the type we are looking for, of course, while preserving type safety. As opposed to the other answers, which will either give you back an Object array or result in warnings at compile time. This approach will gives you a clean solution. The "hack" here is the clazz.cast() call, which compiles without warnings for whatever type you declare an instance of list2Array().
Now, how can you use it?
Simple, just call it like this:
List<String> list = Stream.of("one", "two", "three").collect(Collectors.toList());
String[] numbers = list2Array(String[].class, list);
System.out.println(Arrays.toString(numbers));
Here is the compiling sample of this: https://ideone.com/wcEPNI
Why does it work?
It works because class literals are treated by the compiler as instances of java.lang.Class. This also works for interfaces, enums, any-dimensional arrays (e.g. String[].class), primitives and the keyword void.
Class itself is generic (declared as Class<T[]>, where T[] stands for the type that the Class object is representing), meaning that the type of String[].class is Class<String[]>.
Note: You won't be able to get an array of primitives, since primitives can't be used for type variables.
Effective java 2, Item 42 propose an elegant way for a method take at least one argument, and fail at compile time if the input is empty. The code is shown below in the min() method, however I am wondering what is the elegant way to call this method, because now simply passing list will trigger an compiler error.
public class OneOrMoreArgs {
public static int min(int firstArg, int... remaining){
// but then how do you call the function with a int[] ?
int _min = firstArg;
for(int x: remaining){
if(_min < x ){
_min = x;
}
}
return _min;
}
public static int sum(int... list){
int s = 0;
for(int a: list){
s += a;
}
return s;
}
public static void main(String []args){
int[] list = {1,2,3,4,5};
System.out.println(OneOrMoreArgs.sum(list));
System.out.println(OneOrMoreArgs.min(list));
}
}
Well, the elegant way to call it would be to do
OneOrMoreArgs.min(1, 2, 3, 4, 5);
If you need to pass in an array, you could add an additional method signature like this:
public static int min(int[] args){
if (args == null || args.length < 1) {
throw new IllegalArgumentException("... some error message...");
}
return min(args[0], Arrays.copyOfRange(args, 1, args.length));
}
It needs to be noted that:
This solution is inefficient as it requires the array to be copied.
The check is executed at runtime instead of compile time, so you lose the benefits of the solution proposed by Item 42.
It looks like for your use case, you're better off just declaring a method that takes an array parameter, like the sum() in your example.
This question already has answers here:
Java generics type erasure of method parameters
(3 answers)
Closed 9 years ago.
Consider Following code from Java Puzzlers
class Gloam<T>{
String glom(Collection<?> objs ) {
System.out.println("collection");
String result = "";
for (Object o : objs ){
result += o;
}
return result;
}
int glom(List <Integer> ints ) {
System.out.println("List");
int result = 0;
for ( int i : ints )
result += i ;
return result;
}
public static void main(String[] args) {
List<String> strings = Arrays.asList("1", "2", "3");
System.out.println(new Gloam().glom(strings));
}
}
When I run this program it gives class cast exception, But if I provide any Generic argument for Gloam class in main method it works fine.
public static void main(String[] args) {
List<String> strings = Arrays.asList("1", "2", "3");
System.out.println(new Gloam<Date>().glom(strings));
}
I don't understand how generic works in class type parameter ?
With no generic type passed into constructor, all types are erased and the compiler is presented with this choices
String glom ( Collection );
int glom ( List );
The type is also erased from strings variable defined in main, so its type is List.
Because List is more specific than Collection it chooses int glom ( List ).
Now, if you have specified the generic parameter, then no type erasure happens, and compiler knows that it cannot match int glom ( List<Integer> ) to List<String>, so it falls back to String glom ( Collection<?> )
Once you fail to provide the generic type parameter, all generic typing for the whole class is gone in the eyes of the compiler. The class essentially becomes:
class Gloam<T> {
String glom(Collection objs) {
System.out.println("collection");
String result = "";
for (Object o : objs) {
result += o;
}
return result;
}
int glom(List ints) {
System.out.println("List");
int result = 0;
for (int i : ints)
result += i;
return result;
}
public static void main(String[] args) {
List strings = Arrays.asList("1", "2", "3");
System.out.println(new Gloam().glom(strings));
}
}
So now the compiler will choose the int glom(List ints) override since that's the most-specific override that matches the call. But it will also result in the class cast exception. When you supply the generic parameter, generics are retained and the String glom(Collection<?> objs ) override matches the call (which is passing a List<String>), whereas int glom(List <Integer> ints ) does not match because String is not Integer.
You can use generic to distinguish the method in Java. The JVM doesn't see this type however provided the argument or return type is different it will still compile in the Sun/Oracle compiler. This doesn't compile for the IBM/eclipse compiler.
This shows you want is happening at the byte code level.