【问题标题】:Creating a generic array instance in a generic method在泛型方法中创建泛型数组实例
【发布时间】:2011-05-19 05:47:42
【问题描述】:

我正在尝试构建一个辅助方法来将两行列表转换为单行。我遇到的问题是我不确定如何创建一个 T[] 实例。

我试过了

Array.newInstance(T.class, list.size) 但我不能喂它 T.class..

也试过new T[](list.size),但参数不喜欢。

public <T> T[] ConvertToArray(List<T> list)
{
   T[] result = ???

   result = list.toArray(result);

   return result;
}

还有其他想法吗?

谢谢

【问题讨论】:

  • 感谢您修复我的 ToArray S.P.。您能告诉我我是一个天生的 C# 开发人员吗? :D

标签: java arrays


【解决方案1】:

如果你不能传入T.class,那你基本上就完蛋了。类型擦除意味着您在执行时根本不知道 T 的类型。

当然还有其他指定类型的方法,比如super type tokens——但我猜如果你不能传入T.class,你也不能传入类型标记。如果可以,那就太好了:)

【讨论】:

  • 这对我来说似乎太过分了.. sigh
  • @Ed:已修复,谢谢。 @ShaneC:类型擦除很糟糕,基本上......特别是如果你习惯了 .NET 非常不同的泛型实现。
  • @Jon Skeet 给我一个跨平台/开源版本的 C#,没有 Microsoft 的任何地方,我会立即跳上它
  • @S.P.Floyd:你看过 Mono 吗?
  • @Jon Skeet 当然,几年前(当时看起来很粗糙)。可能应该过一段时间再检查一下
【解决方案2】:

你不能像那样混合泛型和数组。泛型有编译时检查,数组有运行时检查,这些方法大多不兼容。一开始我是这样建议的:

@SuppressWarnings("unchecked")
public <T> T[] ConvertToArray(List<T> list)
{      
   Object[] result = new Object[list.size()];
   result = list.toArray(result);
   return (T[])result;
}

这是隐秘的错误,因为至少这里的另一个人认为它会起作用!但是,当您运行它时,您会收到不兼容的类型错误,因为您不能将 Object[] 转换为 Integer[]。为什么我们不能获得 T.class 并创建一个正确类型的数组?还是new T[]

泛型使用类型擦除来保持向后兼容性。它们在编译时进行检查,但从运行时剥离,因此字节码与预泛型 JVM 兼容。这意味着您无法在运行时获得泛型变量的类知识!

因此,尽管您可以提前保证 T[] result 的类型为 Integer[],但代码 list.toArray(result);(或 new T[],或 Array.newInstance(T.class, list.size());)只会在运行时发生,它无法知道T是什么!

这是一个确实有效的版本,作为阅读该讲座的奖励:

public static <T> T[] convertToArray(List<?> list, Class<T> c) {
    @SuppressWarnings("unchecked")
    T[] result = (T[]) Array.newInstance(c, list.size());
    result = list.toArray(result);
    return (T[]) result;
}

请注意,我们有第二个参数用于在运行时(以及在编译时通过泛型)提供类。你可以这样使用:

Integer[] arrayOfIntegers = convertToArray(listOfIntegers, Integer.class);

这值得麻烦吗?我们仍然需要抑制警告,所以它绝对安全吗?

我的答案是肯定的。那里生成的警告只是来自编译器的“我不确定”。通过单步执行,我们可以确认强制转换总是会成功 - 即使您将错误的类作为第二个参数放入,也会引发编译时警告。

这样做的主要优点是我们将警告集中到一个地方。我们只需要证明这一点是正确的,我们就知道代码总是会成功。引用 Java 文档:

该语言旨在保证如果您的整个应用程序已使用 javac -source 1.5 编译而没有未经检查的警告,则其类型为 safe[1]

因此,现在不是在您的代码中到处都是这些警告,而是在一个地方,您可以使用它而不必担心 - 使用它会大大降低您犯错的风险。

您可能还想查看this SO answer,它更深入地解释了这个问题,以及this answer,这是我写这篇文章时的婴儿床单。除了already cited Java documentation,我使用的另一个方便的参考是this blog post,Neal Gafter 是 Sun Microsystems 的前高级工程师,也是 1.4 和 5.0 语言功能的联合设计师。

当然,还要感谢 ShaneC,他正确地指出我的第一个答案在运行时失败了!

【讨论】:

  • 这个问题是如果你调用它 Integer[] results = convertToArray(listOfIntegers) 你会得到一个错误,你不能将 java.lang.Object 转换为 java.lang。整数
  • ShaneC 是正确的,这不起作用。抱歉,我写这篇文章的时候手头没有 Java 编译器!
  • 感谢这么棒的回答!
【解决方案3】:

问题在于,由于类型擦除,List 在运行时不知道它的组件类型,而组件类型是创建数组所需的。

因此,您可以在整个 API 中找到两个选项:

我能想到的唯一另一种可能性是一个巨大的麻烦:

  • 遍历列表的所有项目并找到“最大公约数”,即所有项目扩展或实现的最具体的类或接口。
  • 创建该类型的数组

但这会比你的两行多得多(而且它也可能导致客户端代码做出无效的假设)。

【讨论】:

  • 请注意,我的方法行不通,所以只有一种选择!
  • 更新:修正了我的方式,但现在与 Jon Skeet 的回答相同。仍然只有一种选择。
  • 这个方法的问题是List&lt;Animal&gt;恰好只包含Cat实例将意味着你返回一个Cat[]而不是Animal[],导致其他一些代码在尝试时失败将Dog 粘贴到结果数组中。
  • 我知道,这正是我上一句话的意思。
【解决方案4】:

这是我能看到的最接近你想做的事。这做出了很大的假设,即您在编写代码时就知道该类,并且列表中的所有对象都是同一个类,即没有子类。我确信这可以变得更复杂,以减轻关于没有子类的假设,但我不明白在 Java 中你如何绕过在编码时知道类的假设。

package play1;

import java.util.*;

public class Play
{
  public static void main (String args[])
  {
    List<String> list=new ArrayList<String>();
    list.add("Hello");
    list.add("Shalom");
    list.add("Godspidanya");

    ArrayTool<String> arrayTool=new ArrayTool<String>();
    String[] array=arrayTool.arrayify(list);

    for (int x=0;x<array.length;++x)
    {
      System.out.println(array[x]);
    }
  }
}
class ArrayTool<T>
{
  public T[] arrayify(List<T> list)
  {
    Class clazz=list.get(0).getClass();
    T[] a=(T[]) Array.newInstance(clazz, list.size());
    return list.toArray(a);
  }
}

【讨论】:

    【解决方案5】:

    这真的需要改为单线吗?对于任何具体的类,您已经可以在一行中进行 List 到 Array 的转换:

    MyClass[] result = list.toArray(new MyClass[0]);
    

    当然,这不适用于类中的泛型参数。

    请参阅 Joshua Bloch 的 Effective Java Second Edition,第 25 项:优先使用列表而不是数组(第 119-123 页)。这是sample chapter PDF 的一部分。

    【讨论】:

    • 你应该做MyClass[] result = list.toArray(new MyClass[list.size()]);,然后你传入的数组可以被复用。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-02-18
    • 2020-04-03
    • 1970-01-01
    • 1970-01-01
    • 2013-02-17
    • 1970-01-01
    • 2010-09-09
    相关资源
    最近更新 更多