如何在 Java 中创建通用数组?

发布于 01-17 22:44 字数 517 浏览 4 评论 0 原文

由于实现了Java Generic,您不能拥有这样的代码:

public class GenSet<E> {
    private E a[];

    public GenSet() {
        a = new E[INITIAL_ARRAY_LENGTH]; // Error: generic array creation
    }
}

在维护类型安全性的同时,我该如何实现?

我在Java论坛上看到了这样的解决方案:

import java.lang.reflect.Array;

class Stack<T> {
    public Stack(Class<T> clazz, int capacity) {
        array = (T[])Array.newInstance(clazz, capacity);
    }

    private final T[] array;
}

这是怎么回事?

Due to the implementation of Java generics, you can't have code like this:

public class GenSet<E> {
    private E a[];

    public GenSet() {
        a = new E[INITIAL_ARRAY_LENGTH]; // Error: generic array creation
    }
}

How can I implement this while maintaining type safety?

I saw a solution on the Java forums that goes like this:

import java.lang.reflect.Array;

class Stack<T> {
    public Stack(Class<T> clazz, int capacity) {
        array = (T[])Array.newInstance(clazz, capacity);
    }

    private final T[] array;
}

What's going on?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(30

ゞ花落谁相伴 2025-01-24 22:44:01

我必须问一个问题:您的 Genset “检查”或“未检查”?
这意味着什么?

  • 检查强键入 Genset 明确知道其包含哪些类型的对象(即,其构造函数被用 class&lt; e&gt; 参数明确调用,并且当方法传递的参数时,方法将引发一个例外不是类型 e .Collection,%20Java.lang.Class%29“ rel =“ noreferrer”> collections.checkedCollection 。。

    - &gt;在这种情况下,您应该写:

     公共类Genset&lt; e&gt; {
    
        私人e [] a;
    
        public Genset(class&lt; e&gt; c,int s){
            //使用数组本机方法创建数组
            //仅在运行时已知的类型
            @suppresswarnings(“未检查”)
            最终e [] a =(e [])array.newinstance(c,s);
            this.a = a;
        }
    
        e get(int i){
            返回[i];
        }
    }
     
  • 未选中的弱打字实际上没有进行任何类型检查。

    - &gt;在这种情况下,您应该写

     公共类Genset&lt; e&gt; {
    
        私人对象[] a;
    
        公共发电机组(int s){
            a =新对象[s];
        }
    
        e get(int i){
            @suppresswarnings(“未检查”)
            最终e e =(e)a [i];
            返回e;
        }
    }
     

    请注意,数组的组件类型应为 类型参数:

     公共类Genset&lt; e扩展了foo&gt; {// E具有Foo的上限
    
        私人foo [] a; // e擦除foo,因此请使用foo []
    
        公共发电机组(int s){
            a = new foo [s];
        }
    
        ...
    }
     

所有这些结果都是由java中的已知且故意的,刻意的,刻意的,含义的弱点:它是使用擦除实现的,所以“仿制药”类不知道它们在运行时创建了什么类型的参数,因此除非实现某些明确的机制(类型检查),否则无法提供类型安全。

I have to ask a question in return: is your GenSet "checked" or "unchecked"?
What does that mean?

  • Checked: strong typing. GenSet knows explicitly what type of objects it contains (i.e. its constructor was explicitly called with a Class<E> argument, and methods will throw an exception when they are passed arguments that are not of type E. See Collections.checkedCollection.

    -> in that case, you should write:

    public class GenSet<E> {
    
        private E[] a;
    
        public GenSet(Class<E> c, int s) {
            // Use Array native method to create array
            // of a type only known at run time
            @SuppressWarnings("unchecked")
            final E[] a = (E[]) Array.newInstance(c, s);
            this.a = a;
        }
    
        E get(int i) {
            return a[i];
        }
    }
    
  • Unchecked: weak typing. No type checking is actually done on any of the objects passed as argument.

    -> in that case, you should write

    public class GenSet<E> {
    
        private Object[] a;
    
        public GenSet(int s) {
            a = new Object[s];
        }
    
        E get(int i) {
            @SuppressWarnings("unchecked")
            final E e = (E) a[i];
            return e;
        }
    }
    

    Note that the component type of the array should be the erasure of the type parameter:

    public class GenSet<E extends Foo> { // E has an upper bound of Foo
    
        private Foo[] a; // E erases to Foo, so use Foo[]
    
        public GenSet(int s) {
            a = new Foo[s];
        }
    
        ...
    }
    

All of this results from a known, and deliberate, weakness of generics in Java: it was implemented using erasure, so "generic" classes don't know what type argument they were created with at run time, and therefore can not provide type-safety unless some explicit mechanism (type-checking) is implemented.

憧憬巴黎街头的黎明 2025-01-24 22:44:01

你可以这样做:

E[] arr = (E[])new Object[INITIAL_ARRAY_LENGTH];

这是在Effective Java中实现通用集合的建议方法之一;第 26 项。没有类型错误,无需重复转换数组。 但是这会触发警告,因为它具有潜在危险,应谨慎使用。正如评论中详细说明的,此 Object[] 现在伪装成我们的 E[] 类型,并且可能会导致意外错误或 ClassCastException,如果使用不安全。

根据经验,只要强制转换数组在内部使用(例如支持数据结构),并且不返回或暴露给客户端代码,这种行为就是安全的。如果您需要将泛型类型的数组返回给其他代码,您提到的反射 Array 类是正确的方法。


值得一提的是,如果您使用泛型,那么只要有可能,您会更愉快地使用 List 而不是数组。当然,有时您别无选择,但使用集合框架要健壮得多。

You can do this:

E[] arr = (E[])new Object[INITIAL_ARRAY_LENGTH];

This is one of the suggested ways of implementing a generic collection in Effective Java; Item 26. No type errors, no need to cast the array repeatedly. However this triggers a warning because it is potentially dangerous, and should be used with caution. As detailed in the comments, this Object[] is now masquerading as our E[] type, and can cause unexpected errors or ClassCastExceptions if used unsafely.

As a rule of thumb, this behavior is safe as long as the cast array is used internally (e.g. to back a data structure), and not returned or exposed to client code. Should you need to return an array of a generic type to other code, the reflection Array class you mention is the right way to go.


Worth mentioning that wherever possible, you'll have a much happier time working with Lists rather than arrays if you're using generics. Certainly sometimes you don't have a choice, but using the collections framework is far more robust.

○愚か者の日 2025-01-24 22:44:01

这是使用仿制药在保存类型安全时的精确类型的方法(与其他答案相反,这要么将您带回 object array或导致警告在编译时):

import java.lang.reflect.Array;  

public class GenSet<E> {  
    private E[] a;  
    
    public GenSet(Class<E[]> type, int length) {  
        a = type.cast(Array.newInstance(type.getComponentType(), length));  
    }  
    
    public static void main(String[] args) {  
        GenSet<String> foo = new GenSet<String>(String[].class, 1);  
        String[] bar = foo.a;  
        foo.a[0] = "xyzzy";  
        String baz = foo.a[0];  
    }  
}

无警告的编译,以及您在 main 中看到的,对于您声明 Genset 的实例,您可以分配 a a 到该类型的数组,您可以将一个元素从 a 分配给该类型的变量,这意味着数组和数组中的值是正确的类型。

java)教程。班级文字被编译器视为 java.lang.class 的实例。要使用一个,只需按照 .class 的类别遵循类的名称。因此, string.class 充当代表类 String 的对象。这也适用于接口,枚举,任何维数组(例如 string []。class ),primitives(例如 int.class )和关键字 void (即 void.class )。

本身是通用的(称为 class&lt; t&gt; ,其中 t 代表 class> class 对象的类型是表示),这意味着 string.class 的类型是 class&lt; string&gt;

因此,每当您调用 GENSET 的构造函数时,您都会在class文字中传递第一个参数,代表 Genset的数组实例的声明类型(例如] .class 用于 Genset&lt; string&gt; )。请注意,您将无法获得一系列原始词,因为原始词不能用于类型变量。

在构造函数内部,调用方法 cast 返回传递的对象参数施放到由 class 对象所示的类表示的类。在 java.lang.reflect.Array 返回作为对象 class表示的类型的数组返回,调用静态方法 newInstance 对象作为第一个参数和由 int 指定的长度传递给了第二个参数。调用方法 getComponentType 返回 class 对象代表由 class class 对象表示该方法的组件类型(例如代码> string.class for string []。class null 如果 class 对象不表示数组)。

最后一句话并不完全准确。调用 string []。class.getComponentType()返回 class 代表类 String 的对象,但其类型是 class&lt;? &gt; ,不是 class&lt; string&gt; ,这就是为什么您不能做以下操作的原因。

String foo = String[].class.getComponentType().cast("bar"); // won't compile

中的每种方法都返回 class 对象。

关于Joachim Sauer在有足够的声誉可以自己对其发表评论),该示例使用铸件对 t [] 将导致警告,因为编译器不能保证在这种情况下键入安全性。


编辑INGO的评论:

public static <T> T[] newArray(Class<T[]> type, int size) {
   return type.cast(Array.newInstance(type.getComponentType(), size));
}

Here's how to use generics to get an array of precisely the type you’re looking for while preserving type safety (as opposed to the other answers, which will either give you back an Object array or result in warnings at compile time):

import java.lang.reflect.Array;  

public class GenSet<E> {  
    private E[] a;  
    
    public GenSet(Class<E[]> type, int length) {  
        a = type.cast(Array.newInstance(type.getComponentType(), length));  
    }  
    
    public static void main(String[] args) {  
        GenSet<String> foo = new GenSet<String>(String[].class, 1);  
        String[] bar = foo.a;  
        foo.a[0] = "xyzzy";  
        String baz = foo.a[0];  
    }  
}

That compiles without warnings, and as you can see in main, for whatever type you declare an instance of GenSet as, you can assign a to an array of that type, and you can assign an element from a to a variable of that type, meaning that the array and the values in the array are of the correct type.

It works by using class literals as runtime type tokens, as discussed in the Java Tutorials. Class literals are treated by the compiler as instances of java.lang.Class. To use one, simply follow the name of a class with .class. So, String.class acts as a Class object representing the class String. This also works for interfaces, enums, any-dimensional arrays (e.g. String[].class), primitives (e.g. int.class), and the keyword void (i.e. void.class).

Class itself is generic (declared as Class<T>, where T stands for the type that the Class object is representing), meaning that the type of String.class is Class<String>.

So, whenever you call the constructor for GenSet, you pass in a class literal for the first argument representing an array of the GenSet instance's declared type (e.g. String[].class for GenSet<String>). Note that you won't be able to get an array of primitives, since primitives can't be used for type variables.

Inside the constructor, calling the method cast returns the passed Object argument cast to the class represented by the Class object on which the method was called. Calling the static method newInstance in java.lang.reflect.Array returns as an Object an array of the type represented by the Class object passed as the first argument and of the length specified by the int passed as the second argument. Calling the method getComponentType returns a Class object representing the component type of the array represented by the Class object on which the method was called (e.g. String.class for String[].class, null if the Class object doesn't represent an array).

That last sentence isn't entirely accurate. Calling String[].class.getComponentType() returns a Class object representing the class String, but its type is Class<?>, not Class<String>, which is why you can't do something like the following.

String foo = String[].class.getComponentType().cast("bar"); // won't compile

Same goes for every method in Class that returns a Class object.

Regarding Joachim Sauer's comment on this answer (I don't have enough reputation to comment on it myself), the example using the cast to T[] will result in a warning because the compiler can't guarantee type safety in that case.


Edit regarding Ingo's comments:

public static <T> T[] newArray(Class<T[]> type, int size) {
   return type.cast(Array.newInstance(type.getComponentType(), size));
}
清浅ˋ旧时光 2025-01-24 22:44:01

这是唯一类型安全的答案:

E[] a;

a = newArray(size);

@SafeVarargs
static <E> E[] newArray(int length, E... array)
{
    return Arrays.copyOf(array, length);
}

This is the only answer that is type-safe:

E[] a;

a = newArray(size);

@SafeVarargs
static <E> E[] newArray(int length, E... array)
{
    return Arrays.copyOf(array, length);
}
祁梦 2025-01-24 22:44:01

要扩展到更多维度,只需添加 [] 的s和Dimension参数到 newInstance() t 是类型参数, cls class&lt; t&gt; d1 通过 d5 是整数):

T[] array = (T[])Array.newInstance(cls, d1);
T[][] array = (T[][])Array.newInstance(cls, d1, d2);
T[][][] array = (T[][][])Array.newInstance(cls, d1, d2, d3);
T[][][][] array = (T[][][][])Array.newInstance(cls, d1, d2, d3, d4);
T[][][][][] array = (T[][][][][])Array.newInstance(cls, d1, d2, d3, d4, d5);

参见 有关详细信息。

To extend to more dimensions, just add []'s and dimension parameters to newInstance() (T is a type parameter, cls is a Class<T>, d1 through d5 are integers):

T[] array = (T[])Array.newInstance(cls, d1);
T[][] array = (T[][])Array.newInstance(cls, d1, d2);
T[][][] array = (T[][][])Array.newInstance(cls, d1, d2, d3);
T[][][][] array = (T[][][][])Array.newInstance(cls, d1, d2, d3, d4);
T[][][][][] array = (T[][][][][])Array.newInstance(cls, d1, d2, d3, d4, d5);

See Array.newInstance() for details.

伴我老 2025-01-24 22:44:01

有效java,第二版,项目25 ... 更喜欢列表而不是阵列

您的代码会产生未检查的警告(您可以用以下注释抑制它:

@SuppressWarnings({"unchecked"})

但是,最好使用列表而不是使用列表而不是数组。

This is covered in Chapter 5 (Generics) of Effective Java, 2nd Edition, item 25...Prefer lists to arrays

Your code will work, although it will generate an unchecked warning (which you could suppress with the following annotation:

@SuppressWarnings({"unchecked"})

However, it would probably be better to use a List instead of an Array.

There's an interesting discussion of this bug/feature on the OpenJDK project site.

十六岁半 2025-01-24 22:44:01

在 Java 8 中,我们可以使用 lambda 或方法引用来创建通用数组。这类似于反射方法(传递Class),但这里我们不使用反射。

@FunctionalInterface
interface ArraySupplier<E> {
    E[] get(int length);
}

class GenericSet<E> {
    private final ArraySupplier<E> supplier;
    private E[] array;

    GenericSet(ArraySupplier<E> supplier) {
        this.supplier = supplier;
        this.array    = supplier.get(10);
    }

    public static void main(String[] args) {
        GenericSet<String> ofString =
            new GenericSet<>(String[]::new);
        GenericSet<Double> ofDouble =
            new GenericSet<>(Double[]::new);
    }
}

例如,它由 <代码> A[] Stream.toArray(IntFunction)

在 Java 8 之前,也可以使用匿名类来完成此操作,但比较麻烦。

In Java 8, we can do a kind of generic array creation using a lambda or method reference. This is similar to the reflective approach (which passes a Class), but here we aren't using reflection.

@FunctionalInterface
interface ArraySupplier<E> {
    E[] get(int length);
}

class GenericSet<E> {
    private final ArraySupplier<E> supplier;
    private E[] array;

    GenericSet(ArraySupplier<E> supplier) {
        this.supplier = supplier;
        this.array    = supplier.get(10);
    }

    public static void main(String[] args) {
        GenericSet<String> ofString =
            new GenericSet<>(String[]::new);
        GenericSet<Double> ofDouble =
            new GenericSet<>(Double[]::new);
    }
}

For example, this is used by <A> A[] Stream.toArray(IntFunction<A[]>).

This could also be done pre-Java 8 using anonymous classes but it's more cumbersome.

说不完的你爱 2025-01-24 22:44:01

您无需将类参数传递给构造函数。
尝试一下。

public class GenSet<T> {

    private final T[] array;

    @SafeVarargs
    public GenSet(int capacity, T... dummy) {
        if (dummy.length > 0)
            throw new IllegalArgumentException(
              "Do not provide values for dummy argument.");
        this.array = Arrays.copyOf(dummy, capacity);
    }

    @Override
    public String toString() {
        return "GenSet of " + array.getClass().getComponentType().getName()
            + "[" + array.length + "]";
    }
}

结果

GenSet<Integer> intSet = new GenSet<>(3);
System.out.println(intSet);
System.out.println(new GenSet<String>(2));

GenSet of java.lang.Integer[3]
GenSet of java.lang.String[2]

You do not need to pass the Class argument to the constructor.
Try this.

public class GenSet<T> {

    private final T[] array;

    @SafeVarargs
    public GenSet(int capacity, T... dummy) {
        if (dummy.length > 0)
            throw new IllegalArgumentException(
              "Do not provide values for dummy argument.");
        this.array = Arrays.copyOf(dummy, capacity);
    }

    @Override
    public String toString() {
        return "GenSet of " + array.getClass().getComponentType().getName()
            + "[" + array.length + "]";
    }
}

and

GenSet<Integer> intSet = new GenSet<>(3);
System.out.println(intSet);
System.out.println(new GenSet<String>(2));

result:

GenSet of java.lang.Integer[3]
GenSet of java.lang.String[2]
心安伴我暖 2025-01-24 22:44:01

Java 泛型的工作原理是在编译时检查类型并插入适当的转换,但会删除编译文件中的类型。这使得泛型库可以被不理解泛型的代码使用(这是一个经过深思熟虑的设计决策),但这意味着您通常无法在运行时找出类型是什么。

公共 Stack(Class clazz,intcapacity) 构造函数要求您在运行时传递一个 Class 对象,这意味着类信息在运行时可用于编写以下代码:需要它。而 Class 形式意味着编译器将检查您传递的 Class 对象是否正是类型 T 的 Class 对象。不是 T 的子类,也不是 T 的超类,而是恰好 T 。

这意味着您可以在构造函数中创建适当类型的数组对象,这意味着您存储在集合中的对象的类型将在将它们添加到集合中时进行类型检查

Java generics work by checking types at compile time and inserting appropriate casts, but erasing the types in the compiled files. This makes generic libraries usable by code which doesn't understand generics (which was a deliberate design decision) but which means you can't normally find out what the type is at run time.

The public Stack(Class<T> clazz,int capacity) constructor requires you to pass a Class object at run time, which means class information is available at runtime to code that needs it. And the Class<T> form means that the compiler will check that the Class object you pass is precisely the Class object for type T. Not a subclass of T, not a superclass of T, but precisely T.

This then means that you can create an array object of the appropriate type in your constructor, which means that the type of the objects you store in your collection will have their types checked at the point they are added to the collection.

别再吹冷风 2025-01-24 22:44:01

泛型用于编译时的类型检查。因此,目的是检查

  • 传入的内容是否是您需要的。
  • 你返回的就是消费者需要的。

检查这个:

在此处输入图像描述

当你编写泛型类时,不要担心类型转换警告;使用时请担心。

Generics are used for type checking during compile time. Therefore, the purpose is to check

  • What comes in is what you need.
  • What you return is what the consumer needs.

Check this:

Enter image description here

Don't worry about typecasting warnings when you are writing a generic class; worry when you are using it.

天冷不及心凉 2025-01-24 22:44:01

这个解决方案怎么样?

@SafeVarargs
public static <T> T[] toGenericArray(T ... elems) {
    return elems;
}

它工作起来而且看起来简单得令人难以置信。有什么缺点吗?

What about this solution?

@SafeVarargs
public static <T> T[] toGenericArray(T ... elems) {
    return elems;
}

It works and looks too simple to be true. Is there any drawback?

扛起拖把扫天下 2025-01-24 22:44:01

该示例是使用Java反射来创建数组。通常不建议这样做,因为它不是typesafe。取而代之的是,您应该做的就是使用内部列表,然后完全避免数组。

The example is using Java reflection to create an array. Doing this is generally not recommended, since it isn't typesafe. Instead, what you should do is just use an internal List, and avoid the array at all.

栀子花开つ 2025-01-24 22:44:01

传递值列表...

public <T> T[] array(T... values) {
    return values;
}

Passing a list of values...

public <T> T[] array(T... values) {
    return values;
}
吃不饱 2025-01-24 22:44:01

还要查看此代码:

public static <T> T[] toArray(final List<T> obj) {
    if (obj == null || obj.isEmpty()) {
        return null;
    }
    final T t = obj.get(0);
    final T[] res = (T[]) Array.newInstance(t.getClass(), obj.size());
    for (int i = 0; i < obj.size(); i++) {
        res[i] = obj.get(i);
    }
    return res;
}

它将任何类型对象的列表转换为相同类型的数组。

Look also to this code:

public static <T> T[] toArray(final List<T> obj) {
    if (obj == null || obj.isEmpty()) {
        return null;
    }
    final T t = obj.get(0);
    final T[] res = (T[]) Array.newInstance(t.getClass(), obj.size());
    for (int i = 0; i < obj.size(); i++) {
        res[i] = obj.get(i);
    }
    return res;
}

It converts a list of any kind of object to an array of the same type.

み零 2025-01-24 22:44:01

我找到了一种对我有用的快速简便的方法。请注意,我仅在Java JDK 8上使用过此功能。我不知道它是否可以与以前的版本一起使用。

尽管我们无法实例化特定类型参数的通用数组,但我们可以将已创建的数组传递给通用类构造函数。

class GenArray <T> {
    private T theArray[]; // Reference array
    
    // ...
    
    GenArray(T[] arr) {
        theArray = arr;
    }

    // Do whatever with the array...
}

现在,我们可以创建类似的数组:

class GenArrayDemo {
    public static void main(String[] args) {
        int size = 10; // array size
        // Here we can instantiate the array of the type we want, say Character (no primitive types allowed in generics)
        Character[] ar = new Character[size];
        
        GenArray<Character> = new Character<>(ar); // Create the generic Array
        
        // ...
        
    }
}

为了更具灵活性,您可以使用链接的列表,例如java.util.arlaylist类中的ArrayList和其他方法。

I have found a quick and easy way that works for me. Note that I have only used this on Java JDK 8. I don't know if it will work with previous versions.

Although we cannot instantiate a generic array of a specific type parameter, we can pass an already created array to a generic class constructor.

class GenArray <T> {
    private T theArray[]; // Reference array
    
    // ...
    
    GenArray(T[] arr) {
        theArray = arr;
    }

    // Do whatever with the array...
}

Now in main we can create the array like so:

class GenArrayDemo {
    public static void main(String[] args) {
        int size = 10; // array size
        // Here we can instantiate the array of the type we want, say Character (no primitive types allowed in generics)
        Character[] ar = new Character[size];
        
        GenArray<Character> = new Character<>(ar); // Create the generic Array
        
        // ...
        
    }
}

For more flexibility with your arrays, you can use a linked list e.g., the ArrayList and other methods found in the Java.util.ArrayList class.

神回复 2025-01-24 22:44:01

我制作了这个代码片段来反射性地实例化一个为简单的自动化测试实用程序传递的类。

Object attributeValue = null;
try {
    if(clazz.isArray()){
        Class<?> arrayType = clazz.getComponentType();
        attributeValue = Array.newInstance(arrayType, 0);
    }
    else if(!clazz.isInterface()){
        attributeValue = BeanUtils.instantiateClass(clazz);
    }
} catch (Exception e) {
    logger.debug("Cannot instanciate \"{}\"", new Object[]{clazz});
}

请注意此段:

    if(clazz.isArray()){
        Class<?> arrayType = clazz.getComponentType();
        attributeValue = Array.newInstance(arrayType, 0);
    }

对于数组初始化,其中 Array.newInstance(数组的类,数组的大小)。类可以是基元 (int.class) 和对象 (Integer.class)。

BeanUtils 是 Spring 的一部分。

I made this code snippet to reflectively instantiate a class which is passed for a simple automated test utility.

Object attributeValue = null;
try {
    if(clazz.isArray()){
        Class<?> arrayType = clazz.getComponentType();
        attributeValue = Array.newInstance(arrayType, 0);
    }
    else if(!clazz.isInterface()){
        attributeValue = BeanUtils.instantiateClass(clazz);
    }
} catch (Exception e) {
    logger.debug("Cannot instanciate \"{}\"", new Object[]{clazz});
}

Note this segment:

    if(clazz.isArray()){
        Class<?> arrayType = clazz.getComponentType();
        attributeValue = Array.newInstance(arrayType, 0);
    }

for array initiating where Array.newInstance(class of array, size of array). Class can be both primitive (int.class) and object (Integer.class).

BeanUtils is part of Spring.

谁的年少不轻狂 2025-01-24 22:44:01

其他人建议的强制转换对我不起作用,引发了非法转换的异常。

然而,这种隐式转换工作得很好:

Item<K>[] array = new Item[SIZE];

其中 Item 是我定义的包含成员的类:

private K value;

这样您就可以获得类型 K 的数组(如果该项目仅具有值)或您想要在类 Item 中定义的任何泛型类型。

The forced cast suggested by other people did not work for me, throwing an exception of illegal casting.

However, this implicit cast worked fine:

Item<K>[] array = new Item[SIZE];

where Item is a class I defined containing the member:

private K value;

This way you get an array of type K (if the item only has the value) or any generic type you want defined in the class Item.

君勿笑 2025-01-24 22:44:01

实际上,一种更简单的方法是创建一个对象数组并将其转换为您想要的类型,如下例所示:

T[] array = (T[])new Object[SIZE];

其中 SIZE 是常量,T 是类型标识符

Actually an easier way to do so, is to create an array of objects and cast it to your desired type like the following example:

T[] array = (T[])new Object[SIZE];

where SIZE is a constant and T is a type identifier

记忆消瘦 2025-01-24 22:44:01

没有其他人回答您发布的示例中发生的情况的问题。

import java.lang.reflect.Array;

class Stack<T> {
    public Stack(Class<T> clazz, int capacity) {
        array = (T[])Array.newInstance(clazz, capacity);
    }

    private final T[] array;
}

正如其他人所说,泛型在编译过程中被“删除”。因此,在运行时,泛型的实例不知道其组件类型是什么。这样做的原因是历史性的,Sun 希望在不破坏现有接口(源代码和二进制)的情况下添加泛型。

另一方面,数组确实在运行时知道它们的组件类型。

此示例通过让调用构造函数(确实知道类型)的代码传递一个参数来告诉类所需的类型来解决该问题。

因此,应用程序将使用类似的内容构造类

Stack<foo> = new Stack<foo>(foo.class,50)

,并且构造函数现在知道(在运行时)组件类型是什么,并且可以使用该信息通过反射 API 构造数组。

Array.newInstance(clazz, capacity);

最后我们进行了类型转换,因为编译器无法知道 Array#newInstance() 返回的数组是正确的类型(即使我们知道)。

这种风格有点丑陋,但有时它可能是创建泛型类型的最不坏的解决方案,这些泛型类型确实需要在运行时了解其组件类型,无论出于何种原因(创建数组或创建其组件类型的实例等)。

No one else has answered the question of what is going on in the example you posted.

import java.lang.reflect.Array;

class Stack<T> {
    public Stack(Class<T> clazz, int capacity) {
        array = (T[])Array.newInstance(clazz, capacity);
    }

    private final T[] array;
}

As others have said generics are "erased" during compilation. So at runtime an instance of a generic doesn't know what its component type is. The reason for this is historical, Sun wanted to add generics without breaking the existing interface (both source and binary).

Arrays on the other hand do know their component type at runtime.

This example works around the problem by having the code that calls the constructor (which does know the type) pass a parameter telling the class the required type.

So the application would construct the class with something like

Stack<foo> = new Stack<foo>(foo.class,50)

and the constructor now knows (at runtime) what the component type is and can use that information to construct the array through the reflection API.

Array.newInstance(clazz, capacity);

Finally we have a type cast because the compiler has no way of knowing that the array returned by Array#newInstance() is the correct type (even though we know).

This style is a bit ugly but it can sometimes be the least bad solution to creating generic types that do need to know their component type at runtime for whatever reason (creating arrays, or creating instances of their component type, etc.).

滥情哥ㄟ 2025-01-24 22:44:01

我找到了解决这个问题的一种方法。

下面的行抛出通用数组创建错误:

List<Person>[] personLists=new ArrayList<Person>()[10];

但是,如果我将 List 封装在一个单独的类中,它就可以工作。

import java.util.ArrayList;
import java.util.List;


public class PersonList {

    List<Person> people;

    public PersonList()
    {
        people=new ArrayList<Person>();
    }
}

您可以通过 getter 公开 PersonList 类中的人员。下面的行将为您提供一个数组,其中每个元素都有一个 List 。换句话说,List 数组。

PersonList[] personLists=new PersonList[10];

我正在编写的一些代码中需要类似的东西,这就是我所做的让它工作。到目前为止还没有任何问题。

I found a sort of a workaround to this problem.

The line below throws generic array creation error:

List<Person>[] personLists=new ArrayList<Person>()[10];

However, if I encapsulate List<Person> in a separate class, it works.

import java.util.ArrayList;
import java.util.List;


public class PersonList {

    List<Person> people;

    public PersonList()
    {
        people=new ArrayList<Person>();
    }
}

You can expose people in the class PersonList through a getter. The line below will give you an array, that has a List<Person> in every element. In other words array of List<Person>.

PersonList[] personLists=new PersonList[10];

I needed something like this in some code I was working on and this is what I did to get it to work. So far there aren't any problems.

原野 2025-01-24 22:44:01

Java中不允许使用通用数组的创建,但是您可以这样做:

class Stack<T> {

    private final T[] array;

    public Stack(int capacity) {
        array = (T[]) new Object[capacity];
    }
}

Generic array creation is disallowed in Java, but you can do it like:

class Stack<T> {

    private final T[] array;

    public Stack(int capacity) {
        array = (T[]) new Object[capacity];
    }
}
久而酒知 2025-01-24 22:44:01

您可以创建一个对象数组并将其强制转换为 E 。是的,这不是很干净的方法,但它至少应该有效。

You could create an Object array and cast it to E everywhere. Yeah, it's not very clean way to do it but it should at least work.

不知在何时 2025-01-24 22:44:01

也许与这个问题无关,但是当我收到“通用数组创建”错误时,

Tuple<Long,String>[] tupleArray = new Tuple<Long,String>[10];

我发现以下工作(并为我工作)与 @SuppressWarnings({"unchecked"} )

 Tuple<Long, String>[] tupleArray = new Tuple[10];

Maybe unrelated to this question but while I was getting the "generic array creation" error for using

Tuple<Long,String>[] tupleArray = new Tuple<Long,String>[10];

I find out the following works (and worked for me) with @SuppressWarnings({"unchecked"}):

 Tuple<Long, String>[] tupleArray = new Tuple[10];
决绝 2025-01-24 22:44:01

你可以使用强制转换:

public class GenSet<Item> {
    private Item[] a;

    public GenSet(int s) {
        a = (Item[]) new Object[s];
    }
}

You could use a cast:

public class GenSet<Item> {
    private Item[] a;

    public GenSet(int s) {
        a = (Item[]) new Object[s];
    }
}
甜心小果奶 2025-01-24 22:44:01

如果您确实想包装固定大小的通用数组,您将有一个方法将数据添加到该数组,因此您可以正确初始化数组,执行如下操作:

import java.lang.reflect.Array;

class Stack<T> {
    private T[] array = null;
    private final int capacity = 10; // fixed or pass it in the constructor
    private int pos = 0;

    public void push(T value) {
        if (value == null)
            throw new IllegalArgumentException("Stack does not accept nulls");
        if (array == null)
            array = (T[]) Array.newInstance(value.getClass(), capacity);
        // put logic: e.g.
        if(pos == capacity)
             throw new IllegalStateException("push on full stack");
        array[pos++] = value;
    }

    public T pop() throws IllegalStateException {
        if (pos == 0)
            throw new IllegalStateException("pop on empty stack");
        return array[--pos];
    }
}

在这种情况下,您使用 java.lang.reflect.Array .newInstance来创建数组,它不会是一个Object[],而是一个真正的T[]。
您不必担心它不是最终的,因为它是在您的班级内部管理的。
请注意,push() 上需要一个非空对象才能获取要使用的类型,因此我添加了对推送的数据的检查并在那里抛出异常。

但这仍然有些毫无意义:您通过推送存储数据,并且方法的签名保证只有 T 元素会进入。因此数组是 Object[] 还是 T[] 或多或少无关紧要。

If you really want to wrap a generic array of fixed size you will have a method to add data to that array, hence you can initialize properly the array there doing something like this:

import java.lang.reflect.Array;

class Stack<T> {
    private T[] array = null;
    private final int capacity = 10; // fixed or pass it in the constructor
    private int pos = 0;

    public void push(T value) {
        if (value == null)
            throw new IllegalArgumentException("Stack does not accept nulls");
        if (array == null)
            array = (T[]) Array.newInstance(value.getClass(), capacity);
        // put logic: e.g.
        if(pos == capacity)
             throw new IllegalStateException("push on full stack");
        array[pos++] = value;
    }

    public T pop() throws IllegalStateException {
        if (pos == 0)
            throw new IllegalStateException("pop on empty stack");
        return array[--pos];
    }
}

in this case you use a java.lang.reflect.Array.newInstance to create the array, and it will not be an Object[], but a real T[].
You should not worry of it not being final, since it is managed inside your class.
Note that you need a non null object on the push() to be able to get the type to use, so I added a check on the data you push and throw an exception there.

Still this is somewhat pointless: you store data via push and it is the signature of the method that guarantees only T elements will enter. So it is more or less irrelevant that the array is Object[] or T[].

悲喜皆因你 2025-01-24 22:44:01

我想知道此代码是否会创建有效的通用数组?

public T [] createArray(int desiredSize){
    ArrayList<T> builder = new ArrayList<T>();
    for(int x=0;x<desiredSize;x++){
        builder.add(null);
    }
    return builder.toArray(zeroArray());
}

//zeroArray should, in theory, create a zero-sized array of T
//when it is not given any parameters.

private T [] zeroArray(T... i){
    return i;
}

如果您需要的大小且小的大小是简单地将所需数量的“ null” s送入ZeroArray命令,则可能是创建这样的数组的另一种方法?

尽管显然这不像使用CreateArray代码那样通用。

I'm wondering if this code would create an effective generic array?

public T [] createArray(int desiredSize){
    ArrayList<T> builder = new ArrayList<T>();
    for(int x=0;x<desiredSize;x++){
        builder.add(null);
    }
    return builder.toArray(zeroArray());
}

//zeroArray should, in theory, create a zero-sized array of T
//when it is not given any parameters.

private T [] zeroArray(T... i){
    return i;
}

Perhaps an alternate way of creating such an array, if the size you required was known and small, would be to simply feed the required number of "null"s into the zeroArray command?

Though obviously this isn't as versatile as using the createArray code.

人海汹涌 2025-01-24 22:44:01

实际上,我找到了一个非常独特的解决方案来绕过无法启动通用数组的问题。您必须创建一个接受通用变量 T 的类,如下所示:

class GenericInvoker <T> {
    T variable;
    public GenericInvoker(T variable){
        this.variable = variable;
    }
}

然后在您的数组类中,使其像这样开始:

GenericInvoker<T>[] array;
public MyArray(){
    array = new GenericInvoker[];
}

启动一个新的 Generic Invoker[] 会导致未经检查的问题但实际上不应该有任何问题。

要从数组中获取数据,您应该像这样调用 array[i].variable:

public T get(int index){
    return array[index].variable;
}

其余的,例如调整数组大小,可以使用 Arrays.copyOf() 来完成,如下所示:

public void resize(int newSize){
    array = Arrays.copyOf(array, newSize);
}

并且可以像这样添加 add 函数:

public boolean add(T element){
    // The variable size below is equal to how many times the add function has been called
    // and is used to keep track of where to put the next variable in the array
    arrays[size] = new GenericInvoker(element);
    size++;
}

I actually found a pretty unique solution to bypass the inability to initiate a generic array. You have to create a class that takes in the generic variable T like so:

class GenericInvoker <T> {
    T variable;
    public GenericInvoker(T variable){
        this.variable = variable;
    }
}

And then in your array class, just have it start like so:

GenericInvoker<T>[] array;
public MyArray(){
    array = new GenericInvoker[];
}

starting a new Generic Invoker[] will cause an issue with unchecked but there shouldn't actually be any issues.

To get from the array you should call the array[i].variable like so:

public T get(int index){
    return array[index].variable;
}

The rest, such as resizing the array can be done with Arrays.copyOf() like so:

public void resize(int newSize){
    array = Arrays.copyOf(array, newSize);
}

And the add function can be added like so:

public boolean add(T element){
    // The variable size below is equal to how many times the add function has been called
    // and is used to keep track of where to put the next variable in the array
    arrays[size] = new GenericInvoker(element);
    size++;
}
行至春深 2025-01-24 22:44:01

数组不支持仿制药(因为它是另一种类型的数据),但是如果您不需要铸造,则可以在创建时使用不确定的仿制药。顺便说一句,它比使用反射要好:

List<?>[] chars = new List[3];

因此,即使没有未经检查的类型警告,我们也将获得法律通用数组。

Arrays do not support generics (because it's another type of data), but you can use undeterminate generics while its creation if you don't need casting. By the way, it's better than using reflection:

List<?>[] chars = new List[3];

So now we are getting the legal generics array even without Unchecked type warning.

乖乖哒 2025-01-24 22:44:01

该语法

GenSet<Integer> intSet[] = new GenSet[3];

方式填充。

for (int i = 0; i < 3; i++)
{
   intSet[i] = new GenSet<Integer>();
}

创建一个空引用数组,以类型安全的

The syntax

GenSet<Integer> intSet[] = new GenSet[3];

creates an array of null references, to be filled as

for (int i = 0; i < 3; i++)
{
   intSet[i] = new GenSet<Integer>();
}

which is type safe.

戴着白色围巾的女孩 2025-01-24 22:44:01

一个简单但混乱的解决方法是在主类中嵌套第二个“holder”类,并使用它来保存数据。

public class Whatever<Thing>{
    private class Holder<OtherThing>{
        OtherThing thing;
    }
    public Holder<Thing>[] arrayOfHolders = new Holder<Thing>[10]
}

An easy, albeit messy workaround to this would be to nest a second "holder" class inside of your main class, and use it to hold your data.

public class Whatever<Thing>{
    private class Holder<OtherThing>{
        OtherThing thing;
    }
    public Holder<Thing>[] arrayOfHolders = new Holder<Thing>[10]
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文