Simplify this generic method to join Java arrays
My goal is to implement a method that connects any number of arrays to a single array of their common supertypes and returns the generated (typed) array I have two realizations
First (this does not need to be simplified):
public static <T> T[] concatArrays(Class<T> type,T[]... arrays) { int totalLen = 0; for (T[] arr: arrays) { arr.getClass().getCom totalLen += arr.length; } T[] all = (T[]) Array.newInstance(type,totalLen); int copied = 0; for (T[] arr: arrays) { System.arraycopy(arr,all,copied,arr.length); copied += arr.length; } return all; }
Let's create some arrays:
Long[] l = { 1L,2L,3L }; Integer[] i = { 4,5,6 }; Double[] d = { 7.,8.,9. };
Our method call:
Number[] n = concatArrays(Number.class,l,i,d);
This is valid and fully type safe (for example, concatarrays (long. Class, d) is a compiler error), but specify number if not necessary Class is a little annoying So I implemented the following method (this is the method I want to simplify):
public static <T> T[] arrayConcat(T[] arr0,T[]... rest) { Class commonSuperclass = arr0.getClass().getComponentType(); int totalLen = arr0.length; for (T[] arr: rest) { totalLen += arr.length; Class compClass = arr.getClass().getComponentType(); while (! commonSuperclass.isAssignableFrom(compClass)) { if (compClass.isAssignableFrom(commonSuperclass)) { commonSuperclass = compClass; break; } commonSuperclass = commonSuperclass.getSuperclass(); compClass = compClass.getSuperclass(); } } T[] all = (T[]) Array.newInstance(commonSuperclass,totalLen); int copied = arr0.length; System.arraycopy(arr0,copied); for (T[] arr: rest) { System.arraycopy(arr,arr.length); copied += arr.length; } return all; }
From the customer's point of view, this works better:
Number[] n = arrayConcat(l,d);
Moreover, the compiler is smart enough to give appropriate errors on long [] all = arrayconcat (L, d) Since the compiler can recognize this error, it is obvious that I perform work at run time (determining the public superclass of a given array) and the compiler can execute at compile time Is there a way to implement my method without using reflection based methods to determine the public superclass of array creation steps?
I tried this method:
public static <T> T[] arrayConcat(T[]... arrays) { int totalLen = 0; for (T[] arr: arrays) { totalLen += arrays.length; } Object[] all = new Object[totalLen]; int copied = 0; for (T[] arr: arrays) { System.arraycopy(arr,arr.length); copied += arr.length; } return (T[]) all; }
But this throws ClassCastException on return Obviously, the new t [totallen] is also out Does anyone have any other ideas?
Solution
You can do this:
public static <T> T[] arrayConcat(T[]... arrays) { int totalLen = 0; for (T[] arr: arrays) { totalLen += arr.length; } T[] all = (T[])Array.newInstance( arrays.getClass().getComponentType().getComponentType(),arr.length); copied += arr.length; } return all; }
This takes advantage of the fact that when using varargs, the compiler constructs the array of components and sets the type of the array correctly so that the component type is the vararg element type In this case, the array type is t []], so we can extract T and use it to construct our T []
(one exception is that if the caller uses a generic type to call it as a varargs type, the compiler cannot construct the correct array type. However, if the caller performs this operation, it will generate a warning (the notorious varargs generic warning) in the caller's code. Since the caller is warned, bad things will happen, so it's not our fault.)
A surprising thing about this solution is that even if the user passes a zero array, it will not produce the wrong answer! (as long as the compiler compiles it successfully, it infers (or explicitly specifies) some specific types T, so that t [] is a valid return type That type T is given to us as an array) obviously, the compiler cannot infer correctly in this case
Note that the caller can manually pass the "arrays" parameter, in which case it can have runtime type u []], where u is a subtype of T In this case, our method will return an array runtime type u [], which is still the correct result, because u [] is a subtype of T []