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&lt;String&gt; list = new ArrayList&lt;String&gt;(); List&lt;Integer&gt; list2 = (List&lt;Integer&gt;)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&lt;String&gt;,因此转换 (T[]) 失败,因为 T 定义为:

T extends Comparable<String>

要解决此问题:

实例化数组,使其项是扩展Comparable&lt;String&gt;的某个类的实例 将 hashTable 从 Array(不是泛型类型)更改为泛型集合类型,例如List&lt;T&gt; hashTable = new ArrayList&lt;T&gt;(tableSize&gt;)

【讨论】:

【参考方案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中的泛型数组的主要内容,如果未能解决你的问题,请参考以下文章

请问下,C#的泛型数组中的 ToDictionary 方法怎么用? 详细说明请进。。

Java中的泛型

Swift 中的泛型数组

数组中的泛型联合

java中的泛型 求详细解释

Java泛型——使用Gson解析复杂的泛型嵌套泛型数据结构