如果我有一个泛型类 Foo<Bar> ,我不允许创建如下数组:
Bar[] bars = new Bar[];
(这将导致错误“无法创建 Bar 的通用数组”)。
但是,正如 dimo414 在对 this question (Java how to: Generic Array creation) 的回答中所建议的那样,我可以执行以下操作:
Bar[] bars = (Bar[]) new Object[];
(这将“仅”生成警告:“类型安全:未检查从 Object[] 到 Bar[] 的转换”)。
在回应dimo414 回答的评论中,有些人声称使用此构造在某些情况下会导致问题,而其他人则说没关系,因为对数组的唯一引用是bars。 ,这已经是所需的类型。
我有点困惑,在哪些情况下这是可以的,在哪些情况下可能会给我带来麻烦。例如,newacct 和 Aaron McDaid 的评论似乎直接相互矛盾。不幸的是,原始问题中的评论流只是以未回答的“为什么这个‘不再正确’?”结尾,所以我决定为此提出一个新问题:
如果bars -array 只包含 Bar 类型的条目,在使用数组或其条目时还会有任何运行时问题吗?或者是唯一的危险,在运行时我可以在技术上将数组转换为其他东西(比如 String[] ),然后我可以用 Bar 以外的类型的值填充它。 ?
我知道我可以使用 Array.newInstance(...)相反,但我对上面的类型转换结构特别感兴趣,因为,例如,在 GWT 中 newInstance(...) -选项不可用。
最佳答案
既然问题中提到了我,我就插话。
基本上,如果不把这个数组变量暴露在类的外部,是不会有什么问题的。 (有点像,维加斯发生的事情留在维加斯。)
数组的实际运行时类型是Object[] .所以把它放到一个 Bar[] 类型的变量中实际上是一个“谎言”,因为 Object[]不是 Bar[] 的子类型(除非 Object 是 Bar )。然而,如果这个谎言留在类里面是可以的,因为 Bar被删除为 Object在类(class)里面。 (在这个问题中,Bar 的下限是 Object。如果 Bar 的下限是其他东西,请将本次讨论中所有出现的 Object 替换为任何界限。)但是,如果这个谎言以某种方式暴露在外面(最简单的例子是将 bars 变量直接返回为类型 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()方法,则此类将是安全的。用这种方法,是不安全的。什么特性使它不安全?它返回 bars作为类型 Bar[] ,这取决于我们之前的“谎言”。由于谎言不是真的,所以会引起问题。如果该方法改为返回类型为 Object[] 的数组,那么它将是安全的,因为它不依赖于“谎言”。
人们会告诉你不要像这样进行强制转换,因为它会在上面看到的意想不到的地方导致强制转换异常,而不是在未经检查的强制转换所在的原始位置。编译器不会警告您 getArray()是不安全的(因为从它的角度来看,给定你告诉它的类型,它是安全的)。因此,这取决于程序员对这个陷阱的勤奋,而不是以不安全的方式使用它。
但是,我认为这在实践中并不是一个大问题。任何设计良好的 API 都不会将内部实例变量暴露给外部。 (即使有返回数组内容的方法,它也不会直接返回内部变量;它会复制它,以防止外部代码直接修改数组。)所以不会像getArray()那样实现任何方法。无论如何。
关于java - 通过未经检查的类型转换在 Java 中创建通用数组,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17831896/
我有多个ActiveRecord子类Item的实例数组,我需要根据最早的事件循环打印。在这种情况下,我需要打印付款和维护日期,如下所示:ItemAmaintenancerequiredin5daysItemBpaymentrequiredin6daysItemApaymentrequiredin7daysItemBmaintenancerequiredin8days我目前有两个查询,用于查找maintenance和payment项目(非排他性查询),并输出如下内容:paymentrequiredin...maintenancerequiredin...有什么方法可以改善上述(丑陋的)代
我的代码目前看起来像这样numbers=[1,2,3,4,5]defpop_threepop=[]3.times{pop有没有办法在一行中完成pop_three方法中的内容?我基本上想做类似numbers.slice(0,3)的事情,但要删除切片中的数组项。嗯...嗯,我想我刚刚意识到我可以试试slice! 最佳答案 是numbers.pop(3)或者numbers.shift(3)如果你想要另一边。 关于ruby-多次弹出/移动ruby数组,我们在StackOverflow上找到一
尝试通过RVM将RubyGems升级到版本1.8.10并出现此错误:$rvmrubygemslatestRemovingoldRubygemsfiles...Installingrubygems-1.8.10forruby-1.9.2-p180...ERROR:Errorrunning'GEM_PATH="/Users/foo/.rvm/gems/ruby-1.9.2-p180:/Users/foo/.rvm/gems/ruby-1.9.2-p180@global:/Users/foo/.rvm/gems/ruby-1.9.2-p180:/Users/foo/.rvm/gems/rub
我需要读入一个包含数字列表的文件。此代码读取文件并将其放入二维数组中。现在我需要获取数组中所有数字的平均值,但我需要将数组的内容更改为int。有什么想法可以将to_i方法放在哪里吗?ClassTerraindefinitializefile_name@input=IO.readlines(file_name)#readinfile@size=@input[0].to_i@land=[@size]x=1whilex 最佳答案 只需将数组映射为整数:@land边注如果你想得到一条线的平均值,你可以这样做:values=@input[x]
为了将Cucumber用于命令行脚本,我按照提供的说明安装了arubagem。它在我的Gemfile中,我可以验证是否安装了正确的版本并且我已经包含了require'aruba/cucumber'在'features/env.rb'中为了确保它能正常工作,我写了以下场景:@announceScenario:Testingcucumber/arubaGivenablankslateThentheoutputfrom"ls-la"shouldcontain"drw"假设事情应该失败。它确实失败了,但失败的原因是错误的:@announceScenario:Testingcucumber/ar
我正在使用puppet为ruby程序提供一组常量。我需要提供一组主机名,我的程序将对其进行迭代。在我之前使用的bash脚本中,我只是将它作为一个puppet变量hosts=>"host1,host2"我将其提供给bash脚本作为HOSTS=显然这对ruby不太适用——我需要它的格式hosts=["host1","host2"]自从phosts和putsmy_array.inspect提供输出["host1","host2"]我希望使用其中之一。不幸的是,我终其一生都无法弄清楚如何让它发挥作用。我尝试了以下各项:我发现某处他们指出我需要在函数调用前放置“function_”……这
这个问题在这里已经有了答案:Checktoseeifanarrayisalreadysorted?(8个答案)关闭9年前。我只是想知道是否有办法检查数组是否在增加?这是我的解决方案,但我正在寻找更漂亮的方法:n=-1@arr.flatten.each{|e|returnfalseife
我正在编写一个gem,我必须在其中fork两个启动两个webrick服务器的进程。我想通过基类的类方法启动这个服务器,因为应该只有这两个服务器在运行,而不是多个。在运行时,我想调用这两个服务器上的一些方法来更改变量。我的问题是,我无法通过基类的类方法访问fork的实例变量。此外,我不能在我的基类中使用线程,因为在幕后我正在使用另一个不是线程安全的库。所以我必须将每个服务器派生到它自己的进程。我用类变量试过了,比如@@server。但是当我试图通过基类访问这个变量时,它是nil。我读到在Ruby中不可能在分支之间共享类变量,对吗?那么,还有其他解决办法吗?我考虑过使用单例,但我不确定这是
我的最终目标是安装当前版本的RubyonRails。我在OSXMountainLion上运行。到目前为止,这是我的过程:已安装的RVM$\curl-Lhttps://get.rvm.io|bash-sstable检查已知(我假设已批准)安装$rvmlistknown我看到当前的稳定版本可用[ruby-]2.0.0[-p247]输入命令安装$rvminstall2.0.0-p247注意:我也试过这些安装命令$rvminstallruby-2.0.0-p247$rvminstallruby=2.0.0-p247我很快就无处可去了。结果:$rvminstall2.0.0-p247Search
我可以得到Infinity和NaNn=9.0/0#=>Infinityn.class#=>Floatm=0/0.0#=>NaNm.class#=>Float但是当我想直接访问Infinity或NaN时:Infinity#=>uninitializedconstantInfinity(NameError)NaN#=>uninitializedconstantNaN(NameError)什么是Infinity和NaN?它们是对象、关键字还是其他东西? 最佳答案 您看到打印为Infinity和NaN的只是Float类的两个特殊实例的字符串