【问题标题】:Generic arrays in JavaJava中的泛型数组
【发布时间】: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 的泛型。有没有办法解决这个问题?

【问题讨论】:

标签: java arrays generics


【解决方案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 中定义的任何泛型类型。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2011-06-13
          • 1970-01-01
          • 2011-12-24
          • 1970-01-01
          • 2011-05-26
          • 2013-06-27
          • 2021-12-01
          相关资源
          最近更新 更多