我正在玩一些代码katas并尝试同时更好地理解java泛型。我有这个小方法打印数组,就像我喜欢看到它们一样,我有几个辅助方法接受一个'事物'数组和一个索引,并返回索引上方或下方的“事物”数组(这是一个二进制搜索算法)。
两个问题,
#1我可以避免在splitBottom和splitTop中转换为T吗?它感觉不对,或者我以错误的方式解决这个问题(不要告诉我使用python或其他东西......;)
#2我是否必须编写单独的方法来处理原始数组,还是有更好的解决方案?
public class Util {
public static <T> void print(T[] array) {
System.out.print("{");
for (int i = 0; i < array.length; i++) {
System.out.print(array[i]);
if (i < array.length - 1) {
System.out.print(", ");
}
}
System.out.println("}");
}
public static <T> T[] splitTop(T[] array, int index) {
Object[] result = new Object[array.length - index - 1];
System.arraycopy(array, index + 1, result, 0, result.length);
return (T[]) result;
}
public static <T> T[] splitBottom(T[] array, int index) {
Object[] result = new Object[index];
System.arraycopy(array, 0, result, 0, index);
return (T[]) result;
}
public static void main(String[] args) {
Integer[] integerArray = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
print(integerArray);
print(splitBottom(integerArray, 3));
print(splitTop(integerArray, 3));
String[] stringArray = {"one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten"};
print(stringArray);
print(splitBottom(stringArray, 3));
print(splitTop(stringArray, 3));
int[] intArray = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
// ???
}
}
答案 0 :(得分:9)
泛型不能以一致的方式处理基元。这是因为泛型不像C ++中的模板,它只是单个类的编译时添加。
编译泛型时,最终会将上面示例中的Object []作为实现类型。作为int []和byte []等,不要扩展Object []你不能互换地使用它们,即使所涉及的代码是相同的(再次泛型不是模板)
唯一的类int []和Object []共享是Object。您可以将上述方法Object作为类型编写(请参阅System.arraycopy,Array.getLength,Array.get,Array.set)
答案 1 :(得分:3)
1我可以避免在splitBottom和splitTop中转换为T吗?它没有 感觉正确,或者我正在谈论这个 错误的方式(不要告诉我使用 python或者其他东西..;))
你不仅可以避免它,而且你不应该这样做。在Java中,不同类型的数组实际上是不同的运行时类型。创建为Object[]
的数组无法分配给AnythingElse []的变量。那里的强制转换不会立即失败,因为在泛型中类型T被删除,但是当代码尝试将它用作Something []时,它将抛出ClassCastException,但是它没有。但是它不是。
解决方案是使用Java 6及更高版本中的Arrays.copyOf...
方法,或者如果您使用的是早期版本的Java,请使用Reflection创建正确的数组类型。例如,
T [] result =(T [])Array.newInstance(array.getClass()。getComponentType(),size);
2我是否必须编写单独的方法来处理原始数组或者是 有更好的解决方案吗?
最好编写单独的方法。在Java中,基本类型的数组与引用类型的数组完全分开;并且没有很好的方法可以同时使用它们。
可以使用Reflection同时处理两者。反射具有Array.get()
和Array.set()
方法,可用于原始数组和引用数组。但是,这样做会失去类型安全性,因为原始数组和引用数组的唯一超类型是Object
。
答案 2 :(得分:1)
问题1: 转换数组不像您期望的那样工作。 String是一个Object,但String数组不是Object数组。
尝试使用以下内容:
public static <T> T[] splitTop(T[] array, int index) {
T[] result = Arrays.copyOfRange(array, index + 1, array.length);
return result;
}
问题2: 对于基元数组,我的功能显然也不起作用。它没有优雅的解决方案 - 例如,查看Arrays库,它为每个基本数组类型提供了几个基本相同的方法副本。
答案 3 :(得分:1)
Java不允许以类型安全的方式构造通用数组。请改用通用序列类型(例如java.util.List
)。
以下是我使用通用容器类fj.data.Stream
编写测试程序的方法:
import fj.data.Stream;
import static fj.data.Stream.range;
// ...
public int[] intArray(Stream<Integer> s) {
return s.toArray(Integer.class).array()
}
public static void main(String[] args) {
Stream<Integer> integerStream = range(1, 10);
print(intArray(integerStream));
print(intArray(integerStream.take(3)));
print(intArray(integerStream.drop(3)));
// ...
}
答案 4 :(得分:0)
你想要完成的任务有两个问题。
首先,您正在尝试使用原始类型,这些类型实际上并不从Object继承。这会弄乱事情。如果你真的需要这样做,请明确使用Integer而不是int等。
第二个也是更大的问题是Java泛型有类型擦除。这意味着在运行时,您实际上无法引用泛型的类型。这样做是为了让你能够混合泛型支持代码和非通用支持代码,并最终(恕我直言)成为Java开发人员头痛的主要来源,并且证明了泛型应该从第1天开始使用Java。我建议你阅读tutorial中关于它的部分,它将使这个问题更加清晰。
答案 5 :(得分:0)
您可能需要将基元包装到相应的集合中。
我还建议您查看Trove原始集合(http://trove.starlight-systems.com)。这与您的泛型问题无关,但可能非常有趣。