为什么我不能用Java创建一个类型参数的数组? [英] Why can't I create an array of a type parameter in Java?

查看:107
本文介绍了为什么我不能用Java创建一个类型参数的数组?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

好吧,我也读了不少这个问题的答案,但我有一个更具体的。以code的片断作为例子。

 公共类GenericArray< E> {
    E [] S =新E [5];
}

类型擦除后,就变成

 公共类GenericArray {
    [对象] S =新对象[5];
}

code的这个片段似乎运作良好。为什么它会导致编译时错误?

在此外,我已经从其他的答案已知以下codeS为同一目的很好地工作。

 公共类GenericArray< E> {
    E [] S =(E [])新对象[5];
}

我读过一些评论说,上面的那块code是不安全的,但为什么是不安全的?谁能给我提供的地方上面这段code会导致一个错误一个具体的例子?

此外,下面的code是错误的为好。但为什么?这似乎擦除之后很好地工作了。

 公共类GenericArray< E> {
        (E S)=新E();
    }


解决方案

  

code的这个片段似乎运作良好。为什么它会导致编译时错误?


首先,因为这将违反类型安全(即它的不安全的 - 见下文),和一般code,可以静态确定这样做是不允许编译

记住的是,由于类型擦除,类型电子不是在运行时称。这位前pression 新E [10] 最多只能创建擦除类型的数组,在这种情况下,对象,使原来的语句:

  E [] S =新E [5];

等同于:

  E [] S =新对象[5];

这肯定是不合法的。例如:

 的String [] S =新的对象[10];

...是不是编译的,对于基本相同的原因。

您认为擦除之后,该语句将是合法的,这意味着你认为这意味着,原来的说法也应视为合法。然而,这是不对的,因为可以用另一个简单的例子显示:

 的ArrayList<串GT; L =新的ArrayList<对象>();

上面会 ArrayList的L =新的ArrayList()的擦除; ,这是合法的,而原来的显然不是

即将它从一个更哲学的角度,类型擦除是不应该改变code的语义,但它会在这种情况下,这样做 - 创建将是一个数组的数组对象,而不是 E的数组(无论电子可能)。存储非 - 电子在它的对象引用这样才有可能,而如果阵列是一个真正的 E [] ,它应该不是产生一个 ArrayStoreException信息


  

为什么是不安全的?


(铭记我们现在谈论的,其中 E [] S =新E [5]的情况下; 已被替换电子[] S =(E [])新对象[5];

这是不安全的(在这种情况下很短的的类型不安全的),因为它在运行时创建了一个情况,即一个变量(取值)持有一个对象实例的引用而不是子类型变量的声明的类型(对象[] 不是 E [] ,除非电子 == 对象)。


  

谁能给我提供的地方上面这段code会导致一个错误一个具体的例子?


根本的问题是,它有可能把非 - 电子对象到您通过执行强制转换(如创建一个数组( E [])新对象[5] )。例如,假设有的方法,它需要一个对象[] 参数,定义如下:

 无效美孚(Object []对象OA){
    OA [0] =新的对象();
}

然后采取以下code:

 的String [] SA =新的String [5];
富(SA);
字符串s = SA [0]; //如果该行达成,S会
                  //肯定指向一个字串(尽管
                  与foo的定义给出//,这
                  //线将无法实现......)

该数组包含绝对字符串甚至调用之后的对象。另一方面:

  E [] EA =(E [])新对象[5];
富(EA);
E E = EA [0]; // E现在可以指非-E对象!

方法可能会插入一个非 - 电子对象到数组。因此,即使在第三行看起来安全,第一个(不安全)线已经违反了保证安全的约束。

一个完整的例子:

 类Foo< E>
{
    无效美孚(对象[] OA){
        OA [0] =新的对象();
    }    公共电子的get(){
        E [] EA =(E [])新对象[5];
        富(EA);
        返回EA [0]; //返回错误的类型
    }
}其他类
{
    公共无效呼我(){
        美孚<串GT; F =新的Foo<>();
        字符串s = f.get();在此*行* // ClassCastException异常
    }
}

运行时,code产生一个​​ClassCastException,这是不是安全的。没有不安全的操作,如强制类型转换code,而另一方面,不能产生这种类型的错误。


  

此外,下面的code是错误的为好。但为什么?这似乎擦除之后很好地工作了。


在code问题:

 公共类GenericArray< E> {
    (E S)=新E();
}

擦除之后,这将是:

 对象S =新的对象();

虽然该行本身将被罚款,对待线为同一将推出,我上面描述的语义变化和安全问题,这也就是为什么,编译器不会接受它。至于为什么它可能会导致问题的例子:

 公开< E> ËgetAnE(){
    返回新E();
}

......因为类型擦除,新E()'将成为'新的对象(),并返回一个非后 - 电子对象从方法明显违反它的类型约束(它应该返回一个电子),因此是的不安全的。如果上述方法都进行编译,并且你把它叫做:

 一个String =<串GT; getAnE();

...那么你会在运行时得到一个类型的错误,因为你会试图分配对象字符串变量。

其它注意事项/澄清:


  • 不安全的(这是短期的类型不安全),意味着它可能会导致code运行时类型的错误,否则将的声音。 (这实际上意味着比这更多,但这个定义就足够了这个答案的目的)。

  • 有可能引起 ClassCastException异常 ArrayStoreException信息或其他异常与安全code,但这些例外只发生在明确的点。这就是说,你通常只能得到一个 ClassCastException异常当您执行强制转换,这本质上带有这种风险的操作。同样的,你只能得到一个 ArrayStoreException信息当一个值存储到一个数组中。

  • 编译器不验证它抱怨说,一个操作是将前实际发生这样的错误的不安全的。它只是知道,某些操作的可能的能引起的问题,并警告这些案件。

  • ,你不能创建一个新的实例(或数组)类型参数既是一种语言功能设计preserve的安全的大概也反映了实施所带来的限制通过使用类型擦除。也就是说,新E()可以预料,以产生实际类型参数的实例,而事实上它只能产生擦除类型的实例。要允许它来编译将是不安全的和潜在的混乱。一般来说,你可以使用电子到位的实际类型,没有不良影响,但这不是实例化的情况。

Well, I have read a lot of answers to this question, but I have a more specific one. Take the following snippet of code as an example.

public class GenericArray<E>{
    E[] s= new E[5];
}

After type erasure, it becomes

public class GenericArray{
    Object[] s= new Object[5];
}

This snippet of code seems to work well. Why does it cause a compile-time error?

In addition, I have known from other answers that the following codes work well for the same purpose.

public class GenericArray<E>{
    E[] s= (E[])new Object[5];
}

I've read some comments saying that the piece of code above is unsafe, but why is it unsafe? Could anyone provide me with a specific example where the above piece of code causes an error?

In addition, the following code is wrong as well. But why? It seems to work well after erasure, too.

public class GenericArray<E>{
        E s= new E();
    }

解决方案

This snippet of code seems to work well. Why does it cause a compile-time error?

First, because it would violate type safety (i.e. it is unsafe - see below), and in general code that can be statically determined to do this is not allowed to compile.

Remember that, due to type erasure, the type E is not known at run-time. The expression new E[10] could at best create an array of the erased type, in this case Object, rendering your original statement:

E[] s= new E[5];

Equivalent to:

E[] s= new Object[5];    

Which is certainly not legal. For instance:

String[] s = new Object[10];

... is not compilable, for basically the same reason.

You argued that after erasure, the statement would be legal, implying that you think this means that the original statement should also be considered legal. However this is not right, as can be shown with another simple example:

ArrayList<String> l = new ArrayList<Object>();

The erasure of the above would be ArrayList l = new ArrayList();, which is legal, while the original is clearly not.

Coming at it from a more philosophical angle, type erasure is not supposed to change the semantics of the code, but it would do so in this case - the array created would be an array of Object rather than an array of E (whatever E might be). Storing a non-E object reference in it would then be possible, whereas if the array were really an E[], it should instead generate an ArrayStoreException.

why is it unsafe?

(Bearing in mind we are now talking about the case where E[] s= new E[5]; has been replaced with E[] s = (E[]) new Object[5];)

It is unsafe (which in this instance is short for type unsafe) because it creates at run-time a situation in which a variable (s) holds a reference to an object instance which is not a sub-type of the variable's declared type (Object[] is not a subtype of E[], unless E==Object).

Could anyone provide me with a specific example where the above piece of code causes an error?

The essential problem is that it is possible to put non-E objects into an array that you create by performing a cast (as in (E[]) new Object[5]). For example, say there is a method foo which takes an Object[] parameter, defined as:

void foo(Object [] oa) {
    oa[0] = new Object();
}

Then take the following code:

String [] sa = new String[5];
foo(sa);
String s = sa[0]; // If this line was reached, s would
                  // definitely refer to a String (though
                  // with the given definition of foo, this
                  // line won't be reached...)

The array definitely contains String objects even after the call to foo. On the other hand:

E[] ea = (E[]) new Object[5];
foo(ea);
E e = ea[0];  // e may now refer to a non-E object!

The foo method might have inserted a non-E object into the array. So even though the third line looks safe, the first (unsafe) line has violated the constraints that guarantee that safety.

A full example:

class Foo<E>
{
    void foo(Object [] oa) {
        oa[0] = new Object();
    }

    public E get() {
        E[] ea = (E[]) new Object[5];
        foo(ea);
        return ea[0];  // returns the wrong type
    }
}

class Other
{
    public void callMe() {
        Foo<String> f = new Foo<>();
        String s = f.get();   // ClassCastException on *this* line
    }
}

The code generates a ClassCastException when run, and it is not safe. Code without unsafe operations such as casts, on the other hand, cannot produce this type of error.

In addition, the following code is wrong as well. But why? It seems to work well after erasure, too.

The code in question:

public class GenericArray<E>{
    E s= new E();
}

After erasure, this would be:

Object s = new Object();

While this line itself would be fine, to treat the lines as being the same would introduce the semantic change and safety issue that I have described above, which is why the compiler won't accept it. As an example of why it could cause a problem:

public <E> E getAnE() {
    return new E();
}

... because after type erasure, 'new E()' would become 'new Object()' and returning a non-E object from the method clearly violates its type constraints (it is supposed to return an E) and is therefore unsafe. If the above method were to compile, and you called it with:

String s = <String>getAnE();

... then you would get a type error at runtime, since you would be attempting to assign an Object to a String variable.

Further notes / clarification:

  • Unsafe (which is short for "type unsafe") means that it could potentially cause a run-time type error in code that would otherwise be sound. (It actually means more than this, but this definition is enough for purposes of this answer).
  • it's possible to cause a ClassCastException or ArrayStoreException or other exceptions with "safe" code, but these exceptions only occur at well defined points. That is, you can normally only get a ClassCastException when you perform a cast, an operation that inherently carries this risk. Similarly, you can only get an ArrayStoreException when you store a value into an array.
  • the compiler doesn't verify that such an error will actually occur before it complains that an operation is unsafe. It just knows that that certain operations are potentially able to cause problems, and warns about these cases.
  • that you can't create a new instance of (or an array of) a type parameter is both a language feature designed to preserve safety and probably also to reflect the implementation restrictions posed by the use of type erasure. That is, new E() might be expected to produce an instance of the actual type parameter, when in fact it could only produce an instance of the erased type. To allow it to compile would be unsafe and potentially confusing. In general you can use E in place of an actual type with no ill effect, but that is not the case for instantiation.

这篇关于为什么我不能用Java创建一个类型参数的数组?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆