通过未经检查的类型转换在 Java 中创建通用数组 [英] Creating generic array in Java via unchecked type-cast

查看:38
本文介绍了通过未经检查的类型转换在 Java 中创建通用数组的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

如果我有一个泛型类Foo<Bar>,则不允许我创建如下数组:

If I have a generic class Foo<Bar>, I am not allowed to create an array as follows:

Bar[] bars = new Bar[];

(这将导致错误无法创建 Bar 的通用数组").

(This will cause the error "Cannot create a generic array of Bar").

但是,正如 dimo414 在对 的回答中所建议的那样这个问题(Java how to: Generic Array creation),我可以做到以下几点:

But, as suggested by dimo414 in an answer to this question (Java how to: Generic Array creation), I can do the following:

Bar[] bars = (Bar[]) new Object[];

(这将仅"生成一个警告:类型安全:未检查从 Object[] 到 Bar[] 的强制转换").

(This will "only" generate a warning: "Type safety: Unchecked cast from Object[] to Bar[]").

在回应 dimo414 的回答的评论中,有些人声称在某些情况下使用这种构造会导致问题,而其他人则说这很好,因为对数组的唯一引用是 条形,已经是所需的类型.

In the comments responding to dimo414's answer, some people claim that using this construct can cause problems in certain situations and others say it's fine, as the only reference to the array is bars, which is of the desired type already.

我有点困惑,在哪些情况下这是可以的,在哪些情况下可能会给我带来麻烦.例如,newacctAaron McDaid 的评论似乎直接相互矛盾.不幸的是,原始问题中的评论流只是以未回答的为什么这'不再正确'?"结束,所以我决定为此提出一个新问题:

I'm a little confused in which cases this is OK and in which cases it can run me into trouble. The comments by newacct and Aaron McDaid, for example, seem to directly contradict each other. Unfortunately the comment stream in the original question simply ends with the unanswered "Why is this 'no longer correct'?", so I decided to make a new question for it:

如果 bars-array 只包含 Bar 类型的条目,那么在使用该数组或其条目时还会有任何运行时问题吗?或者是唯一的危险,在运行时我可以在技术上将数组转换为其他内容(例如 String[]),然后允许我用除 String[] 以外的类型的值填充它代码>条形?

If the bars-array only ever contains entries of type Bar, could there still be any run-time issues when using the array or its entries? Or is the only danger, that at run-time I could technically cast the array to something else (like String[]), which would then allow me to fill it with values of a type other than Bar?

我知道我可以使用 Array.newInstance(...) 代替,但我对上面的类型转换结构特别感兴趣,因为,例如,在 GWT 中 newInstance(...) - 选项不可用.

I know I can use Array.newInstance(...) instead, but I am specifically interested in the type-casting construct above, since, for example, in GWT the newInstance(...)-option isn't available.

推荐答案

既然问题中提到了我,我就插话.

Since I was mentioned in the question, I will chime in.

基本上,如果你不把这个数组变量暴露给类的外部,它不会造成任何问题.(有点像,在维加斯发生的事情留在维加斯.)

Basically, it will not cause any problems if you don't expose this array variable to the outside of the class. (kinda like, What happens in Vegas stays in Vegas.)

数组的实际运行时类型是Object[].因此,将它放入 Bar[] 类型的变量实际上是一个谎言",因为 Object[] 不是 Bar[] 的子类型> (除非 ObjectBar).但是,这个谎言如果留在类内部是可以的,因为 Bar 在类内部被擦除为 Object.(本题中Bar的下界是Object.如果Bar的下界是别的,替换所有出现的Object 在这个讨论中与边界是什么.)但是,如果这个谎言以某种方式暴露在外面(最简单的例子是将 bars 变量直接返回为类型 Bar[],那么就会出问题.

The actual runtime type of the array is Object[]. So putting it into a variable of type Bar[] is effectively a "lie", since Object[] is not a subtype of Bar[] (unless Object is Bar). However, this lie is okay if it stays inside the class, since Bar is erased to Object inside the class. (The lower bound of Bar is Object in this question. In a case where the lower bound of Bar is something else, replace all occurrences of Object in this discussion with whatever that bound is.) However, if this lie gets somehow exposed to the outside (the simplest example is returning the bars variable directly as type Bar[], then it will cause problems.

要了解到底发生了什么,查看带有和不带有泛型的代码是有益的.任何泛型程序都可以重写为等效的非泛型程序,只需删除泛型并在正确的位置插入强制转换即可.这种转换称为类型擦除.

To understand what is really going on, it is instructive to look at the code with and without generics. Any generics program can be re-written into an equivalent non-generics program, simply by removing generics and inserting casts in the right place. This transformation is called type erasure.

我们考虑一个简单的 Foo 实现,包括获取和设置数组中特定元素的方法,以及获取整个数组的方法:

We consider a simple implementation of Foo<Bar>, with methods for getting and setting particular elements in the array, as well as a method for getting the whole array:

class Foo<Bar> {
    Bar[] bars = (Bar[])new Object[5];
    public Bar get(int i) {
        return bars[i];
    }
    public void set(int i, Bar x) {
        bars[i] = x;
    }
    public Bar[] getArray() {
        return bars;
    }
}

// in some method somewhere:
Foo<String> foo = new Foo<String>();
foo.set(2, "hello");
String other = foo.get(3);
String[] allStrings = foo.getArray();

类型擦除后,这变成:

class Foo {
    Object[] bars = new Object[5];
    public Object get(int i) {
        return bars[i];
    }
    public void set(int i, Object x) {
        bars[i] = x;
    }
    public Object[] getArray() {
        return bars;
    }
}

// in some method somewhere:
Foo foo = new Foo();
foo.set(2, "hello");
String other = (String)foo.get(3);
String[] allStrings = (String[])foo.getArray();

所以班级内不再有演员表了.但是,在调用代码中存在强制转换——获取一个元素和获取整个数组时.获取一个元素的转换不应该失败,因为我们唯一可以放入数组的东西是Bar,所以我们唯一能取出的东西也是Bar.但是,获取整个数组时的转换将失败,因为数组具有实际的运行时类型 Object[].

So there are no casts inside the class anymore. However, there are casts in the calling code -- when getting one element, and getting the entire array. The cast to get one element should not fail, because the only things we can put into the array are Bar, so the only things we can get out are also Bar. However, the cast when getting the entire array, that will fail, since the array has actual runtime type Object[].

非一般地编写,正在发生的事情和问题变得更加明显.特别令人不安的是,转换失败不会发生在我们用泛型编写转换的类中——它发生在使用我们类的其他人的代码中.而那个人的代码是完全安全和无辜的.它也不会在我们在泛型代码中进行转换时发生——它发生在稍后,当有人在没有警告的情况下调用 getArray() 时.

Written non-generically, what is happening and the problem become much more apparent. What is especially troubling is that the cast failure does not happen in the class where we wrote the cast in generics -- it happens in someone else's code that uses our class. And that other person's code is completely safe and innocent. It also does not happen at the time where we did our cast in the generics code -- it happens later, when someone calls getArray(), without warning.

如果我们没有这个 getArray() 方法,那么这个类就是安全的.用这种方法,是不安全的.什么特性使它不安全?它返回 bars 作为 Bar[] 类型,这取决于我们之前制作的谎言".由于谎言是不真实的,它会引起问题.如果该方法以 Object[] 类型返回数组,那么它是安全的,因为它不依赖于谎言".

If we didn't have this getArray() method, then this class would be safe. With this method, it is unsafe. What characteristic makes it unsafe? It returns bars as type Bar[], which depends on the "lie" we made earlier. Since the lie is not true, it causes problems. If the method had instead returned the array as type Object[], then it would be safe, since it does not depend on the "lie".

人们会告诉你不要像这样执行这样的转换,因为它会在如上所示的意外位置导致转换异常,而不是在未经检查的转换所在的原始位置.编译器不会警告您 getArray() 是不安全的(因为从它的角度来看,鉴于您告诉它的类型,它是安全的).因此,这取决于程序员是否认真对待这个陷阱,不要以不安全的方式使用它.

People will tell you to not do such a cast like this, because it causes cast exceptions in unexpected places as seen above, not in the original place where the unchecked cast was. The compiler will not warn you that getArray() is unsafe (because from its point of view, given the types you told it, it is safe). Thus it depends on the programmer to be diligent about this pitfall and not to use it in an unsafe way.

然而,我认为这在实践中并不是一个大问题.任何设计良好的 API 都不会将内部实例变量暴露给外部.(即使有方法以数组形式返回内容,也不会直接返回内部变量,而是复制,防止外部代码直接修改数组.)所以不会像那样实现方法getArray() 无论如何.

However, I would argue that this is not a big concern in practice. Any well-designed API will never expose internal instance variables to the outside. (Even if there is a method to return the contents as an array, it would not return the internal variable directly; it would copy it, to prevent outside code from modifying the array directly.) So no method will be implemented like getArray() is anyway.

这篇关于通过未经检查的类型转换在 Java 中创建通用数组的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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