如何转换List& T& gt;使用泛型方法将数组t [](对于基本类型)设置为?
我正在使用泛型方法进行一些测试,我想仅用一个(convertListToArray)转换下面的这两种方法(convertFloatListToArray和convertShortListToArray):
I am doing some tests with generic-methods and I'd like to transform these two methods below (convertFloatListToArray and convertShortListToArray) in just one (convertListToArray):
public class Helper{
public static float[] convertFloatListToArray(List<Float> list){
float[] array = new float[list.size()];
for(int i = 0; i<list.size(); i++){
array[i] = list.get(i);
}
return array;
}
public static short[] convertShortListToArray(List<Short> list){
short[] array = new short[list.size()];
for(int i = 0; i<list.size(); i++){
array[i] = list.get(i);
}
return array;
}
}
但是当我尝试使用泛型时,如下所示,我出现了一些错误:
But when I try to use generics, as below, I have some errors:
public class Helper{
public static <T, E> T convertListToArray(List<E> list){
T array = new T[list.size()];
for(int i = 0; i<list.size(); i++){
array[i] = list.get(i);
}
return array;
}
}
我可以理解有关泛型的java限制,但是我想知道是否有人知道我没有看到的任何使用泛型方法的解决方案.
I can understand java limitations about generics, but I wonder if someone know any solution, using generic-method, that I am not seeing.
从当前版本(Java 12)开始,基本类型不能用Java泛型表示.更具体地说,我们不能提供原始类型作为类型实参.(我们不能执行 Foo< int>
.)我们也不能将类型变量用作 new
表达式中的类型,因此我们不能执行 new T [n]
创建一个数组.因此,没有理想的方法来做到这一点.
As of the current version (Java 12), primitive types can't be represented with Java generics. More specifically, we can't provide a primitive type as a type argument. (We can't do e.g. Foo<int>
.) We also can't use type variables as the type in a new
expression, so we can't do new T[n]
to create an array. Therefore, there's no ideal way to do this.
It is possible to do this reasonably using some reflection (java.lang.reflect.Array
), but we need to provide a Class
as an argument. Here's an example of how it might be done:
/**
* Unboxes a List in to a primitive array.
*
* @param list the List to convert to a primitive array
* @param arrayType the primitive array type to convert to
* @param <P> the primitive array type to convert to
* @return an array of P with the elements of the specified List
* @throws NullPointerException
* if either of the arguments are null, or if any of the elements
* of the List are null
* @throws IllegalArgumentException
* if the specified Class does not represent an array type, if
* the component type of the specified Class is not a primitive
* type, or if the elements of the specified List can not be
* stored in an array of type P
*/
public static <P> P toPrimitiveArray(List<?> list, Class<P> arrayType) {
if (!arrayType.isArray()) {
throw new IllegalArgumentException(arrayType.toString());
}
Class<?> primitiveType = arrayType.getComponentType();
if (!primitiveType.isPrimitive()) {
throw new IllegalArgumentException(primitiveType.toString());
}
P array = arrayType.cast(Array.newInstance(primitiveType, list.size()));
for (int i = 0; i < list.size(); i++) {
Array.set(array, i, list.get(i));
}
return array;
}
示例调用:
List<Integer> list = List.of(1, 2, 3);
int[] ints = toPrimitiveArray(list, int[].class);
Note that Array.set
will perform a widening primitive conversion, so the following works:
List<Integer> list = List.of(1, 2, 3);
double[] doubles = toPrimitiveArray(list, double[].class);
但是它不会执行缩小转换,因此以下内容会引发异常:
But it won't perform a narrowing conversion, so the following throws an exception:
List<Integer> list = List.of(1, 2, 3);
byte[] bytes = toPrimitiveArray(list, byte[].class); // throws
如果您愿意,该代码还可用于简化复制操作:
If you wanted, that code could also be used to make duplication easier:
public static int[] toIntArray(List<Integer> list) {
return toPrimitiveArray(list, int[].class);
}
public static double[] toDoubleArray(List<Double> list) {
return toPrimitiveArray(list, double[].class);
}
...
(不过,具有多种类似方法并不是真正的泛型.)
(Having multiple methods like that isn't really generic, though.)
您有时会看到地点的一种解决方案如下所示:
One solution that you'll sometimes see places looks something like this:
public static <P> P toPrimitiveArray(List<?> list) {
Object obj0 = list.get(0);
Class<?> type;
// "unbox" the Class of obj0
if (obj0 instanceof Integer)
type = int.class;
else if (obj0 instanceof Double)
type = double.class;
else if (...)
type = ...;
else
throw new IllegalArgumentException();
Object array = Array.newInstance(type, list.size());
for (int i = 0; i < list.size(); i++) {
Array.set(array, i, list.get(i));
}
return (P) array;
}
尽管如此,它还是有很多问题:
There are a variety of problems with that, though:
- 如果列表为空,我们不知道要创建哪种类型的数组.
- 如果列表中的对象类型超过一种,则不起作用.
- 未经检查将结果数组强制转换为
P
,因此存在堆污染的危险./li>
- We don't know what type of array to create if the list is empty.
- Doesn't work if there's more than one type of object in the list.
- Unchecked casting of the result array to
P
, so there's a danger of heap pollution.
最好只传入 Class
作为参数.
此外,尽管可以编写许多重载来取消数组装箱:
Also, while it's possible to just write many overloads which unbox arrays:
public static int[] unbox(Integer[] arr) {...}
public static long[] unbox(Long[] arr) {...}
public static double[] unbox(Double[] arr) {...}
...
由于类型擦除的影响,无法编写重载,该重载将许多不同类型的 List
拆箱,如下所示:
Because of the effects of type erasure, it's impossible to write overloads which unbox many different types of List
, as in the following:
public static int[] unbox(List<Integer> list) {...}
public static long[] unbox(List<Long> list) {...}
public static double[] unbox(List<Double> list) {...}
...
那不会编译,因为在同一个类中,我们不允许有多个具有相同名称和擦除的方法.这些方法必须具有不同的名称.
That won't compile, because we aren't allowed to have more than one method in the same class with the same name and erasure. The methods would have to have different names.
作为旁注,以下是一些非通用解决方案:
As a side-note, here are some non-generic solutions:
-
从Java 8开始,我们可以使用以下命令取消对
Integer
,Long
和Double
的List
的装箱Stream
API:
As of Java 8 we can unbox
List
s ofInteger
,Long
andDouble
using theStream
API:
List<Long> list = List.of(1L, 2L, 3L);
long[] longs = list.stream().mapToLong(Long::longValue).toArray();
Google Guava在其 Doubles.toArray
:
List<Double> list = List.of(1.0, 2.0, 3.0);
double[] doubles = Doubles.toArray(list);