Java中的泛型数组
Posted
技术标签:
【中文标题】Java中的泛型数组【英文标题】:Generic arrays in Java 【发布时间】:2022-01-17 02:36:17 【问题描述】:好的,我一直在网上搜索,但似乎找不到任何解决问题的方法。我找到了很多解决方案,但没有一个适合。
我需要创建一个泛型数组。但是泛型类型本身扩展了 Comparable。当我尝试以下操作时:
public class Hash<T extends Comparable<String>>
private T[] hashTable;
private int tableSize;
Hash(int records, double load)
tableSize = (int)(records / loadFactor);
tableSize = findNextPrime(tableSize);
hashTable = (T[])(new Object[tableSize]); //Error: Ljava.lang.Object; cannot be cast to [Ljava.lang.Comparable;
问题在于不能将 Object 转换为扩展 Comparable 的泛型。有没有办法解决这个问题?
【问题讨论】:
只有丑陋的解决方案。 IMO 是 Java 的众多问题之一。 你的意思是那些不懂语言的人^ Java how to: Generic Array creation的可能重复 【参考方案1】:泛型和数组基本上不能混用。简短的回答是您可以解决此问题。更长的答案是您可能不应该这样做,我会解释原因。
你可以像这样使用Array.newInstance()
:
private Comparable[] hashtable;
...
hashtable = (Comparable[])Array.newInstance(Comparable.class, tableSize);
但您不能创建参数化类型的数组。
数组是协变的。这意味着它们在运行时保留其元素的类型。 Java 的泛型不是。他们使用 type erasure 基本上掩盖了正在进行的隐式转换。理解这一点很重要。
因此,当您创建 Object 数组时,您不能将其转换为 Comparable 数组(或任何其他类型),因为这是不正确的。
给你一个例子。对于泛型,这是完全合法的:
List<String> list = new ArrayList<String>();
List<Integer> list2 = (List<Integer>)list;
list.add(3);
这也是你不能这样做的原因:
public <T> T newInstance(T t)
return new T(); // error!
即在运行时不知道 T 的类。这就是为什么上面的代码更常写成:
public <T> T newInstance(T t, Class<T> clazz)
return clazz.newInstance();
因为它们不是泛型参数的运行时类型。但是对于数组:
String arr[] = new String[10];
Integer arr2[] = (Integer[])arr; // error!
在这种情况下(恕我直言)你应该做的不是使用数组,而是使用ArrayList
。老实说,几乎没有理由在 ArrayList
上使用数组,泛型只是其中的一个例子。
如需更好更完整的解释,请参阅(优秀)Java Generics FAQ:
能否创建一个组件类型为具体参数化类型的数组?
不,因为它不是类型安全的。
数组是协变的,这意味着 超类型引用数组是 子类型数组的超类型 参考。也就是说,
Object[]
是String[]
的超类型和一个字符串 数组可以通过一个访问Object[]
类型的引用变量。...
【讨论】:
downvote - 使用 java.lang.reflect.Array.newInstance 查看答案 非常感谢!这是一个很好的解释,但在网络上的其他地方更令人费解。谢谢!(Comparable[])Array.newInstance(Comparable.class, tableSize)
与new Comparable[tableSize]
具有完全相同相同的效果
这里有一些错误。 List<String> list = new ArrayList<String>(); List<Integer> list2 = (List<Integer>)list;
不编译。 ie at runtime there is no knowledge of T's class
您不能实例化 T 的实例,因为在 编译 时您不知道 T 的类型。
"数组是协变的。这意味着它们在运行时保留其元素的类型。Java 的泛型不是"不是协变的,而是可具体化的 (docs.oracle.com/javase/specs/jls/se7/html/jls-4.html#jls-4.7)【参考方案2】:
这里的其他答案通常都提倡更好的方法(特别是建议使用 ArrayList 代替),但在这种特定情况下的简单答案可能是:
hashTable = (T[])(new Comparable[tableSize]);
(即创建一个原始 Comparable 类型的数组,而不是 Object)
如果您正确地将对该数组的所有访问封装在您的 Hash 对象中,这应该可以工作,但是(正如其他答案所解释的那样)您可能会让自己容易受到攻击。
【讨论】:
【参考方案3】:你正在尝试的演员表
(T[])(new Object[tableSize]);
失败,因为数组中的项目是 Object 的实例。对象没有扩展Comparable<String>
,因此转换 (T[]) 失败,因为 T 定义为:
T extends Comparable<String>
要解决此问题:
实例化数组,使其项是扩展Comparable<String>
的某个类的实例
将 hashTable
从 Array(不是泛型类型)更改为泛型集合类型,例如List<T> hashTable = new ArrayList<T>(tableSize>)
【讨论】:
【参考方案4】:当您需要实例化泛型类型的东西时,您经常会遇到问题。解决这个问题的最简单方法是传递实际将存储在构造函数中的类。这样您就可以从实际类型构造。试试这样的:
public class Hash<T extends Comparable<String>>
Hash(int records, double load, Class<T> class)
tableSize = (int)(records / loadFactor);
tableSize = findNextPrime(tableSize);
hashTable = java.lang.reflect.Array.newInstance(class, tableSize);
private T[] hashTable;
private int tableSize;
【讨论】:
【参考方案5】:其他人建议的强制转换对我不起作用,抛出非法转换异常。
但是,这种隐式转换效果很好:
Item<K>[] array = new Item[SIZE];
其中 Item 是我定义的包含该成员的类:
private K value;
这样你得到一个类型为 K 的数组(如果项目只有值)或你想在类 Item 中定义的任何泛型类型。
【讨论】:
以上是关于Java中的泛型数组的主要内容,如果未能解决你的问题,请参考以下文章