java 在泛型方法中创建泛型数组实例

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/4337187/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-30 05:56:59  来源:igfitidea点击:

Creating a generic array instance in a generic method

javaarrays

提问by Shane Courtrille

I'm trying to build a helper method to turn the two line list to array conversion into a single line. The problem I've ran into is that I'm not sure how to create a a T[] instance.

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

I've tried

我试过了

Array.newInstance(T.class, list.size)but I can't feed it T.class..

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

Also tried new T[](list.size)but it doesn't like the parameters.

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

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

   result = list.toArray(result);

   return result;
}

Any other ideas?

还有其他想法吗?

Thanks

谢谢

回答by ZoFreX

You can't mix generics and arrays like that. Generics have compile-time checking, arrays have runtime checking, and those approaches are mostly incompatible. At first I suggested this:

你不能像那样混合泛型和数组。泛型有编译时检查,数组有运行时检查,而这些方法大多不兼容。起初我建议这样做:

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

This is wrong in a stealthy way, as at least one other person on here thought it would work! However when you run it you get an incompatible type error, because you can't cast an Object[] to an Integer[]. Why can't we get T.class and create an array the right type? Or do new T[]?

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

Generics use type erasureto preserve backward compatibility. They are checked at compile time, but stripped from the runtime, so the bytecode is compatible with pre-generics JVMs. This means you cannot have class knowledge of a generic variable at runtime!

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

So while you can guarantee that T[] resultwill be of the type Integer[] ahead of time, the code list.toArray(result);(or new T[], or Array.newInstance(T.class, list.size());) will only happen at runtime, and it cannot know what T is!

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

Here's a version that doeswork, as a reward for reading that lecture:

下面是一个版本的工作,作为报酬,用于读取演讲:

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;
}

Note that we have a second parameter to provide the class at runtime (as well as at compile time via generics). You would use this like so:

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

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

Is this worth the hassle? We still need to suppress a warning, so is it definitely safe?

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

My answer is yes. The warning generated there is just an "I'm not sure" from the compiler. By stepping through it, we can confirm that that cast will always succeed - even if you put the wrong class in as the second parameter, a compile-time warning is thrown.

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

The major advantage of doing this is that we have centralised the warning to one single place. We only need to prove this one place correct, and we know the code will always succeed. To quote the Java documentation:

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

the language is designed to guarantee that if your entire application has been compiled without unchecked warnings using javac -source 1.5, it is type safe[1]

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

So now rather than having these warnings all over your code, it's just in one place, and you can use this without having to worry - there's a massively reduced risk of you making a mistake by using it.

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

You may also want to look at this SO answerwhich explains the issue in more depth, and this answerwhich was my crib sheet when writing this. As well as the already cited Java documentation, another handy reference I used was this blog postby Neal Gafter, ex senior staff engineer at Sun Microsystems and co-designer of 1.4 and 5.0's language features.

您可能还想查看这个 SO 答案,它更深入地解释了这个问题,而这个答案是我写这篇文章时的婴儿床。除了已经引用的 Java 文档之外,我使用的另一个方便的参考是Neal Gafter 的这篇博文,他是 Sun Microsystems 的前高级工程师,也是 1.4 和 5.0 语言特性的共同设计者。

And of course, thanks to ShaneC who rightly pointed out that my first answer failed at runtime!

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

回答by Jon Skeet

If you can't pass in T.class, then you're basically screwed. Type erasure means you simply won't know the type of Tat execution time.

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

Of course there are other ways of specifying types, such as super type tokens- but my guess is that if you can't pass in T.class, you won't be able to pass in a type token either. If you can, then that's great :)

当然,还有其他指定类型的方法,例如超类型标记- 但我的猜测是,如果您不能传入T.class,您也将无法传入类型标记。如果可以,那就太好了:)

回答by Sean Patrick Floyd

The problem is that because of type erasure, a List does not know it's component type at runtime, while the component type is what you would need to create the array.

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

So you have the two options you will find all over the API:

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

The only other possibility I can think of would be a huge hassle:

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

  • Iterate over all items of the list and find the "Greatest Common Divisor", the most specific class or interface that all the items extend or implement.
  • Create an array of that type
  • 遍历列表中的所有项并找到“最大公约数”,即所有项扩展或实现的最具体的类或接口。
  • 创建该类型的数组

But that's going to be a lot more lines than your two (and it may also lead to client code making invalid assumptions).

但这将比您的两行多得多(并且还可能导致客户端代码做出无效假设)。

回答by Powerlord

Does this really need to be changed to a one-liner? With any concrete class, you can already do a List to Array conversion in one line:

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

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

Granted, this won't work for generic arguments in a class.

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

See Joshua Bloch's Effective Java Second Edition, Item 25: Prefer lists to array (pp 119-123). Which is part of the sample chapter PDF.

请参阅 Joshua Bloch 的Effective Java Second Edition,第 25 项:首选列表而不是数组(第 119-123 页)。这是示例章节 PDF 的一部分

回答by Jay

Here's the closest I can see to doing what you want. This makes the big assumptions that you know the class at the time you write the code and that all the objects in the list are the same class, i.e. no subclasses. I'm sure this could be made more sophisticated to relieve the assumption about no subclasses, but I don't see how in Java you could get around the assumption of knowing the class at coding time.

这是我能看到的最接近做你想做的事。这做出了很大的假设,即您在编写代码时知道类,并且列表中的所有对象都是同一个类,即没有子类。我确信这可以做得更复杂,以消除关于没有子类的假设,但我不知道在 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);
  }
}