gpt4 book ai didi

why does "Integer [ ] x = (Integer [ ]) new Object[100]; " compile?(为什么要编译“Integer[]x=(Integer[])new Object[100];”?)

转载 作者:bug小助手 更新时间:2023-10-26 20:57:16 32 4
gpt4 key购买 nike



I compile:

我编写了:


Integer [ ] x = (Integer [ ]) new Object[100];

it succeeds.
and I don't know Why?

它成功了。我不知道为什么?


更多回答

Because you lied to the compiler.

因为你对编译器撒了谎。

Casting is a convenience Java has given you to make the Java compiler trust you. So, it compiles but you are trying to abuse the convenience given to you. Remember Karma 😊

强制转换是Java为您提供的便利,可以让Java编译器信任您。所以,它可以编译,但您正在试图滥用给予您的便利。还记得Karma😊吗

Note that casting a literal expression happens at compile time: int i = (int)(1.0+2.0); This is different than casting a reference type as the OP has. So the OP might be asking why the reference type is different than the literal type.

请注意,强制转换文字表达式在编译时发生:int i=(Int)(1.0+2.0);这不同于像OP那样强制转换引用类型。因此,OP可能会问为什么引用类型不同于文本类型。

优秀答案推荐

It compiles. But it does not run. Try it - shove that in a main method and execute it. You'll get a ClassCastException.

它会编译。但它不能运行。尝试一下--将其放入Main方法并执行。您将获得一个ClassCastException。


There are a number of things going on here. Most of this is 'programming language archeology' - none of it makes sense and none of it is particularly consistent. If your aim is to learn the language better, there's a very simple rule you should apply to your java programming:

这里发生了许多事情。其中大部分是“编程语言考古学”--没有一个是有意义的,也没有一个是特别一致的。如果您的目标是更好地学习这门语言,那么有一条非常简单的规则应该应用到您的Java编程中:



  1. Do not use arrays. At all. For anything. Except perhaps arrays of primitive types (int[], long[], etcetera) for performance reasons. There is no point to them. They aren't faster than lists and have all sorts of wonky downsides. Including effectively broken equals and hashCode and toString implementations (in that they do unexpected things; the spec properly explains these things, but an API that does what the spec says it does, but the spec says bizarro unexpected things, well, I'll let the reader come up with a term for it. I trust it won't be a particularly nice term), they are this useless amalgamation of immutable-ish (they can neither grow nor shrink) but not actually immutable (you can set values), and more.



  2. When casting arrays, assume that the compiler has no idea and you'll have to rely on runtime exceptions. Really, once you've reached this level (You're casting arrays), you need to back up and fix whatever got you into that mess.




There is really not much point in going any further if your aim is to be a good java coder.

如果你的目标是成为一名优秀的Java程序员,那么再往前走就没有多大意义了。


But, hey, you asked - so, let's put on our brown hat, grab a whip, play the Indiana Jones themesong, and go spelunkin' into the depths of java's distant past!

但是,嘿,你问了--那么,让我们戴上我们的棕色帽子,拿着鞭子,演奏印第安纳·琼斯的主题曲,去探索爪哇遥远的过去的深处!


Covariance, Contravariance, Invariance.


We need to cover the key problem first. An answer has already been posted by somebody who does not understand type systems, so, that's anecdotal proof this needs to be explained first.

我们需要首先解决关键问题。不了解类型系统的人已经发布了答案,因此,这是需要首先解释的轶事证据。


Java's type system is itself 'covariant'. That means: Any type is a fine substitution for any of its supertypes. If you have a method that requires that you pass it a Number parameter, you can pass an expression of type Integer, no problem.

Java的类型系统本身是“协变的”。这意味着:任何类型都可以很好地替代它的任何超类型。如果有一个方法需要向其传递Number参数,则可以传递Integer类型的表达式,这是没有问题的。


However, once you move up to listy things (be it generics, or arrays) this does not work. They should be invariant - Only the exact type required will do and no other type can substitute.

然而,一旦您升级到列表内容(无论是泛型,还是数组),这就不起作用了。它们应该是不变的-只有所需的确切类型可以,没有其他类型可以替代。


That's because you can actually both write and read from listy things and therein lies the problem. Here's a trivial example:

这是因为您实际上可以从列表中写入和读取内容,这就是问题所在。下面是一个简单的例子:


Integer i = 5;
Number n = i; // this is fine.

Integer[] is = new Integer[10];
Number[] ns = is; // fine, too? right? Maybe?
ns[0] = 5.5; // ... a double
Integer firstElement = is[0]; // BOOM!

That last line shows the problem. Because basic java types are covariant and will remain so, the ns[0] = 5.5 line has to compile - I'm assigning a Double to a place that requires a Number. Nevertheless this leads to a problem on the last line.

最后一行说明了问题。因为基本的java类型是协变的,并且将保持不变,所以ns[0] = 5.5行必须编译-我将Double分配给需要Number的地方。然而,这导致了最后一行的问题。


Generics gets this right. The generics equivalent is this:

泛型在这方面做得很好。泛型的等价物如下:


List<Integer> is = new ArrayList<Integer>();
List<Number> ns = is; // This does not compile!

And gives you the tools you need to make nice APIs; you can ask for covariance (List<? extends Number>, where a List<Integer> is acceptable, but the compiler prevents you from writing to this list to ensure the 'whoops there is a double in my list of integers' problem cannot occur), and even contravariance (List<? super Integer>, where you can pass a List<Integer>, or List<Number>, or a List<Object>. You can write to it (any integer - which works fine regardless of which of those 3 kinds of lists is actually passed in), but the compiler prevents you from reading. Or rather, returns Object, given that List in general guarantees that whatever it stores, it's at least Object).

并为您提供制作优秀API所需的工具;您可以要求协方差(List<?扩展数字>,其中列表 是可接受的,但编译器阻止您写入此列表,以确保不会出现“哎呀,我的整数列表中有一个双精度”问题),甚至是逆变(列表<?Super Integer>,可以传递List 、List 或List 。您可以对其进行写入(任何整数--无论实际传入的是哪种列表,它都可以很好地工作),但编译器会阻止您读取。或者更确切地说,返回Object,因为该列表通常保证它存储的内容至少是Object)。


Here's a good tip: When you read List<? extends Number> you may be translating that: "A list, containing things whose type is Number or subtypes of Number - you know, things whose type is.. something, that extends Number". That is wrong. You're supposed to read it as: "A list, limited to contain some type that I simply do not know here (see the question mark? That means 'don't know'). All I know is: That restriction is Number or some subtype there of. It could be a list that is only supposed to store integers. Could be Doubles. Could be any Number. No idea. Everything I do with this list must be valid regardless of whether that's a List<Number> or a List<Integer> or a List<Double> or any other List<X> where X is a subtype of Number. It has to work for ALL of them or it is not valid java and the compiler will refuse it. Hence why list.add() doesn't work here - there is no value that is both Double and Integer and Number and SomeSubTypeOfNumberYouHaventEvenWrittenYet. Well, except the literal null which indeed you can actually pass, the only way to call add at all.

这里有一个很好的提示:当你阅读清单<?扩展数字>你可能会翻译成:“一个列表,包含类型是数字的东西或数字的子类型--你知道的,类型是..的东西,扩展数字的东西”。这是不对的。你应该把它读成:“一个列表,被限制为包含一些我在这里完全不知道的类型(参见问号?这意味着“不知道”)。我所知道的是:该限制是数字或其中的某个子类型。它可以是一个应该只存储整数的列表。可能是双打。可以是任何数字。不知道。我对这个列表所做的任何事情都必须有效,无论它是List 、List 、List 还是任何其他List ,其中X是Numbers的子类型。它必须对所有Java都有效,否则它就不是有效的Java,编译器将拒绝它。因此,list.add()在这里不起作用--没有同时是Double和Integer以及Numbers和SomeSubTypeOfNumberYouHaventEvenWrittenYet的值。好的,除了你实际上可以传递的空字面值,这是调用Add的唯一方法。


Arrays get this totally wrong.

数组完全搞错了。


Why? Well, generics is really complicated. The notion that you need 4 different types that all are List<Number> adjacent but subtly different is already quite complex:

为什么?嗯,仿制药真的很复杂。您需要4种不同的类型,它们都列出 相邻但略有不同,这一概念已经相当复杂:



  • List (raw/legacy type. Perhaps if java had had generics from v1.0 this would not be needed).

  • List<Number>

  • List<? extends Number>

  • List<? super Number>


and java was trying to 'keep things simple' so didn't add this. Generics didn't show up until java 1.5, over a decade later.

而Java试图让事情变得简单,所以没有添加这一点。十多年后,泛型直到Java 1.5才出现。


In some ways the designers of java were apparently (it's been a looong time, I haven't asked them) also not aware that the component type of a listy thing just cannot be covariant. Or, more likely, sometimes you want to write code that operates on arbitrary arrays and the language designers decided to just let the compiler get out of the way, turn java into more or less a dynamically typed (i.e. the compiler doesn't help you) situation where only the runtime exists to prevent problems from occurring.

在某些方面,Java的设计者显然(已经很久了,我还没有问过他们)也没有意识到列表的组件类型不可能是协变的。或者,更有可能的是,有时您想编写在任意数组上操作的代码,而语言设计者决定让编译器让开,将Java或多或少地变成一种动态类型(即编译器不能帮助您)的情况,其中只存在运行时以防止问题发生。


Hence, you can do this stuff:

因此,您可以这样做:


Object is = new Integer[10];
Number[] numbers = (Number[]) is;
is[0] = 5.5;

That code compiles, but when you run it.. the second line works, but the third line doesn't - fails with an ArrayStoreException. That's because arrays do know what their component type is, and thus the is[0] =.. assignment does check: Hey, what's the actual component type of the array object that the is variable is pointing at? Oh it's Integer[].. hmm, then, ArrayStoreException. It's a variant of ClassCastException in that sense.

该代码可以编译,但当您运行它时..第二行可以用,但第三行不行--失败,抛出一个ArrayStoreException。这是因为数组确实知道它们的组件类型,因此is[0]=..赋值会检查:嘿,is变量所指向的数组对象的实际组件类型是什么?哦,是Integer[]..嗯,那么,数组存储异常。从这个意义上说,它是ClassCastException的一个变体。


Similarly, you can actually cast Object arrays to whatever array type you want, but the system is designed so that storing things into arrays might fail with an ArrayStoreException, but reading them never will. Because it's easier to reason about code when you know certain things aren't possible. Such as non-integers being in your Integer[] array.

类似地,您实际上可以将对象数组转换为您想要的任何数组类型,但系统的设计是这样设计的:将数据存储到数组中可能会失败,并引发ArrayStoreException,但读取它们永远不会失败。因为当您知道某些事情是不可能的时,更容易对代码进行推理。例如Integer[]数组中的非整数。


To ensure that rule, when you cast an array, the runtime checks and won't let you if it means read operations could fail. Hence why arrays are covariant-ish:

为了确保该规则,当您强制转换数组时,运行库进行检查,如果这意味着读取操作可能失败,则不会允许您这样做。因此,数组是协变的原因:


Object xs = new Integer[100];
Number[] ns = (Number[]) xs; // allowed
xs = new Number[100];
Integer[] is = (Integer[]) xs; // not allowed

Where 'not allowed' means: Usually compiles fine, but at runtime results in a ClassCastException.

其中‘不允许’意味着:通常编译得很好,但在运行时会导致ClassCastException异常。


The only reason it works that way is that the 'allowed' line is still broken (in that you'd expect an array of type Number[] to allow you to write any Number to it, whereas here this one doesn't and will fail with an ArrayStoreEx unless it's an Integer), but allowed because it can't break when reading from it (whatever is in that array, it's at least a Number. Because all Integers are Numbers and the only thing that array can store is Integer objects). Whereas the second is not allowed, because if it had been allowed, reading from it could cause problems. ArrayStoreException exists; ArrayReadException does not.

它以这种方式工作的唯一原因是‘Allowed’行仍然是断开的(因为您期望类型为Numbers[]的数组允许您向它写入任何数字,而在这里,除非它是一个Integer,否则这个类型的数组不会也将使用ArrayStoreEx失败),但是允许,因为它在读取它时不能断开(无论该数组中是什么,它至少是一个数字)。因为所有的整数都是数字,而数组唯一可以存储的是Integer对象)。而第二个是不允许的,因为如果允许,从它读取可能会产生问题。ArrayStoreException存在;ArrayReadException不存在。


Why was the choice made this way? At some point we've arrived at a dead end. The answer to that is: "Because the spec says so" and if you want to know why that is the case, the answer is: "Because the designers at the time wanted it that way". Any further 'but.. why?' questions should be directed directly at James Gosling and co.

为什么会做出这样的选择?在某种程度上,我们已经走到了死胡同。这个问题的答案是:“因为规格说明是这样说的”,如果你想知道为什么会这样,答案是:“因为当时的设计师希望它是这样的”。还有什么要问的吗,但是……为什么?问题应该直接问詹姆斯·高斯林和他的同事。


更多回答

1/5th through this answer and I already could tell who the author was. Contravariance/Covariance/Invariance are key topics that are not easy to understand. Well stated, and from all of us, thanks for well educating us. 1+

通过这个答案,我已经知道作者是谁了。逆方差/协方差/不变性是不容易理解的关键主题。说得好,来自我们所有人,感谢你对我们的良好教育。1+

I'm not sure this answers the OP's question. Why does that line compile? The compiler will evaluate int i = 1.0 + 2.0; at compile time and throw an error. But I'm pretty sure both the cast (Integer[]) and the new keyword are never evaluated by the compiler like a literal is. (But I didn't double check that.) So there is no choice: both new and the cast are only evaluated at runtime.

我不确定这能不能回答特工的问题。为什么要编译这行代码?编译器将在编译时计算int i=1.0+2.0;并抛出错误。但是我非常确定CAST(Integer[])和new关键字都不会像文本那样由编译器进行计算。(但我没有仔细检查这一点。)因此别无选择:new和强制转换都只在运行时求值。

I guess in other words I'm interpreting the OP's question as asking "The type Object[] is right there, the cast (Integer[]) is right there, why can't the compiler evaluate that and spot the error immediately?" (Edit: but int i = (int)(1.0+2.0); is evaluated at compile time, so I guess casting references only happens at runtime, primitives can be evaluated at compile time; possibly this is also a source of confusion.)

我想换句话说,我将OP的问题解释为在问“类型对象[]就在那里,类型转换(Integer[])就在那里,为什么编译器不能立即对其求值并发现错误?”(编辑:但是int i=(Int)(1.0+2.0);是在编译时求值的,所以我猜强制转换引用只在运行时发生,原语可以在编译时求值;这可能也是混淆的一个来源。)

The problem will be with ns[0] = 5.5; // ... a double, not with Integer firstElement = is[0]; // BOOM!.

问题将是ns[0]=5.5;//...一个Double,不带Integer FirstElement=is[0];//轰!

@markspace The last line of the answer explains it: It's a one-word explanation: "Because". Spec says so. Doing it right is very complicated (see middle of answer: Co/Contra/Invariance), presumably java v1.0 design threw in the towel to try to make sense of it at compile time.

@markspace答案的最后一行解释了这一点:这是一个词的解释:因为。斯派克是这么说的。正确地做它是非常复杂的(参见答案的中间部分:CO/Contra/Invarance),大概是为了在编译时理解它而放弃了Java v1.0设计。

32 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com