加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 编程开发 > Java > 正文

通过未经检查的类型转换在Java中创建通用数组

发布时间:2020-12-14 17:44:17 所属栏目:Java 来源:网络整理
导读:如果我有一个通用类Foo Bar,我不允许如下创建一个数组: Bar[] bars = new Bar[]; (这将导致错误“无法创建一个通用的Bar数组”). 但是,根据dimo414在this question (Java how to: Generic Array creation)的答案中的建议,我可以做以下事情: Bar[] bars = (
如果我有一个通用类Foo< Bar>,我不允许如下创建一个数组:
Bar[] bars = new Bar[];

(这将导致错误“无法创建一个通用的Bar数组”).

但是,根据dimo414在this question (Java how to: Generic Array creation)的答案中的建议,我可以做以下事情:

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

(这将“仅”生成一个警告:“键入安全性:从Object []到Bar []”的未检查的转换).

在回应dimo414的回答的评论中,有些人声称使用这个构造可能会导致某些情况下的问题,而其他人则表示很好,因为对阵列的唯一参考是已经有所需类型的条形.

我有点困惑,在这种情况下,这是可以的,在哪些情况下,可以让我陷入困境.例如,newacct和Aaron McDaid的评论似乎直接相互矛盾.不幸的是,原始问题中的评论流只是以未解答的“为什么这不再正确”结束?所以我决定提出一个新的问题:

如果bars-array仅包含条目类型Bar,则在使用数组或其条目时是否仍然存在运行时问题?或者是唯一的危险,在运行时,我可以在技术上将数组转换为其他的东西(如String []),这将允许我填充除Bar之外的类型的值?

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

解决方法

自从我在提到这个问题以后,我会进来的.

基本上,如果不将此数组变量公开到类的外部,则不会引起任何问题. (有点像拉斯维加斯的拉斯维加斯发生什么事情)

数组的实际运行时类型是Object [].所以把它放在一个Bar []类型的变量中,实际上是一个“谎言”,因为Object []不是Bar []的子类型(除非Object是Bar).然而,如果它停留在课堂内,这个谎言是可以的,因为Bar被擦除到Class里面的Object. (在这个问题中,Bar的下限是Object,在Bar的下限是其他的情况下,在本次讨论中将所有出现的对象替换为所有限制.)然而,如果这个谎言以某种方式暴露于外部(最简单的例子是直接返回bar变量,如Bar []类型,则会导致问题.

要了解真正发生的是什么,使用和不使用泛型来查看代码是有启发性的.任何仿制程序都可以重新编写成等效的非泛型程序,只需通过删除泛型代码并将代码插入正确的位置即可.这种转换称为类型擦除.

我们考虑使用Foo< Bar>的简单实现,以及用于获取和设置数组中的特定元素的方法以及获取整个数组的方法:

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 [].

书面的一般而言,发生了什么,问题变得更加明显.特别令人担忧的是,在我们用泛型写的演员的类中,没有发生演员失败 – 它发生在使用我们的类的别人的代码中.而该人的守则是完全安全无辜的.它也不会发生在我们在泛型代码中进行转换的时候 – 它会在以后发生,当有人调用getArray()时,没有警告.

如果我们没有这个getArray()方法,那么这个类将是安全的.使用这种方法,这是不安全的.什么特点使它不安全?它返回作为Bar []类型的条,这取决于我们之前所做的“谎言”.由于谎言不正确,造成问题.如果方法已经将数组返回为Object []类型,那么它将是安全的,因为它不依赖于“lie”.

人们会告诉你不要这样做,因为它会在上面出现的意外的地方引起异常,而不是在未被选中的演员的原始地方.编译器不会警告你getArray()是不安全的(因为从它的角度来看,给定你告诉它的类型,它是安全的).因此,这取决于程序员对这个陷阱的努力,而不是以不安全的方式使用它.

但是,我认为这不是一个很大的问题.任何精心设计的API都不会将内部实例变量暴露给外界. (即使有一种方法来返回内容作为数组,它也不会直接返回内部变量;它会复制它,以防止外部代码直接修改数组)所以没有像getArray()无论如何

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读