8

这是我正在使用的代码

public class aClass<T> {

    private T[] elements;

    public aClass(T[] elements) {
        this.elements = elements;
    }

    public void doSomething() {
        T[] newArray = (T[]) new Object[5];
        ...
    }

}

我见过有人说创建这样的数组是个坏主意,因为它不是类型安全的。但是,每次我使用它时,我都没有遇到任何问题。什么时候创建这样的数组会导致问题?

谢谢

4

7 回答 7

4

您不能创建 T 数组,因为 Java 在运行时不知道 T 的类型是什么。这是因为在 Java 中泛型是通过类型擦除实现的。这意味着编译器在确保一切正常后会丢弃大部分泛型类型信息。

对于数组,情况就不同了,因为 Java 需要知道 T 的确切类型才能创建给定的数组,并且由于无法确定此类事情,因此您无法创建泛型类型的数组。

您可以做的是提供您要使用的实际数组的实例,Java 编译器可以确保它是适当的类型:

public static <T> void fillWith(T[] destiny, List<? extends T> source){
    for(int i=0; i<= destiny.length; i++){
        destiny[i] = source.get(i);
    }
}

java.utils.Arrays.copy方法提供了一种谨慎使用泛型和反射的替代方法,您可以将其用作您想要做的事情的参考。

public static <T> T[] copyOf(T[] original, int newLength) {
   return (T[]) copyOf(original, newLength, original.getClass());
}


public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
        T[] copy = ((Object)newType == (Object)Object[].class)
            ? (T[]) new Object[newLength]
            : (T[]) Array.newInstance(newType.getComponentType(), newLength);
        System.arraycopy(original, 0, copy, 0,
                         Math.min(original.length, newLength));
        return copy;
}
于 2012-04-05T16:11:20.150 回答
4

这是一个导致问题的示例:

public class Main {

    public static class List<E extends Number> {

        private E[] myE;

        public void hello(E e) {
            E[] newArray = (E[]) new Object[5];
            for (int i = 0; i < newArray.length; i++) {
                newArray[i] = e;
            }
            myE = newArray;
        }

    }

    public static <T> T[] createArray(int size) {
        return (T[]) new Object[size];
    }

    public static void main(String[] args) throws IOException {
        List<Integer> integers = new List<Integer>();
        integers.hello(5);
    }
}

您的代码有效,因为当您声明泛型参数<T>时,它是未绑定的,这意味着它扩展了Object. 当您将数组转换为时,(T[])您实际上是在转换为,(Object[])因为这是编译器可以做的最好的事情。现在,如果您将数组保留在代码中,您应该不会遇到太多问题。但是,如果您的代码之外的某个人可以检索该数组并使用对象以外的类型实例化您的类,他将有 ClassCastException。

于 2012-04-05T16:12:19.643 回答
2

不是类型安全的东西本身不会产生问题。但它可以在编译时隐藏问题,直到正确的时刻才会弹出。

您将能够在非类型安全的环境中完全工作而不会出现问题,但这是一个坏习惯,因为类型安全的环境确实可以保证您不会遇到一组常见的运行时错误,您没有任何目前,但您没有任何保证,这些确实很重要,您可以信任的任何安全都意味着更少的努力。

于 2012-04-05T15:55:26.843 回答
1

其他人都错了。除非您在创建时拥有该类型的实例,否则没有其他方法可以创建数组。另请参阅如何在 Java 中创建泛型数组?

如果你有类型实例(= 具有类型的东西Class<T>),你可以调用java.lang.reflect.Array.newInstance(Class<?>, int),但即便如此,你也需要强制转换,那何必呢?

elements如果类型是Object而不是,我会说情况会有所不同,T但由于情况并非如此,因此代码完全可以。

如果你想进一步隐藏它,你可以编写一个辅助方法:

  @SuppressWarnings( "unchecked" )
  public static <T> T[] createArray( int size ) {
      return (T[]) new Object[size];
  }

这会在您调用它时创建一个数组而无需强制转换。

于 2012-04-05T15:56:57.887 回答
1

Collection在这种情况下使用 a 更安全。做类似的东西

...
Collection<T> newElements = new ArrayList<T>(5);
...

无论如何,据我所知,创建这样的通用数组不会给您带来真正的问题,但“闻起来很糟糕”,因为它需要显式类型转换。在这种情况下你真的需要一个数组吗?

于 2012-04-05T16:00:56.720 回答
1

我见过有人说创建这样的数组是个坏主意,因为它不是类型安全的。但是,每次我使用它时,我都没有遇到任何问题。什么时候创建这样的数组会导致问题?

人们说这是一个坏主意,因为从逻辑上讲,将运行时类型为 if is not 的对象强制转换是Object[]T[]正确TObject

但是,您看不到任何直接的问题,因为在类内部(在 的范围内TT被删除了。所以不检查从Object[]to的演员表。T[]

如果你所做的只是在你的类中使用这个变量(在类型变量的范围内T),并且你永远不会将它返回给类外的人,并且类外的人无法访问它,那么它不会引起任何问题。

事实上,你这样做的方式是有好处的——在获取和设置数组元素时,你可以获得泛型类型检查的所有好处,如果你只是遵循完全的“类型安全”,你就不会得到这些好处使用类型变量的解决方案Object[],在这种情况下,您将不得不手动转换您从中获得的东西。

如果您将此数组变量作为 type 返回到外部(或以其他方式允许外部访问它),您将遇到问题T[],因为调用代码将期望某种类型的数组,并且泛型将在调用中插入强制转换接收到这个数组时的代码,并且强制转换将在运行时失败(因为例如 aObject[]不是 a String[];它们在运行时是不同的类型)。这是一个简单的例子:

public class aClass<T> {

    private T[] elements;

    public aClass(T[] elements) {
        this.elements = elements;
    }

    public void doSomething() {
        elements = (T[]) new Object[5];
        ...
    }

    public T[] getArray() {
        return elements;
    }

}

// some other code...
aClass<String> foo = new aClass<String>(new String[2]);
foo.doSomething();
String[] bar = foo.getArray(); // ClassCastException here
于 2012-04-06T00:37:28.950 回答
0

您可能在比较和打印方面遇到问题.. 基本上任何时候知道类型对于格式化很重要。你拥有它的方式系统不知道数组中有什么数据

于 2012-04-05T15:48:51.480 回答