java 为什么要避免铸造?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/4167304/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Why should casting be avoided?
提问by LoudNPossiblyWrong
I generally avoid casting types as much as possible since I am under the impression that it's poor coding practice and may incur a performance penalty.
我通常会尽可能避免强制转换类型,因为我认为这是糟糕的编码实践并且可能会导致性能损失。
But if someone asked me to explain why exactly that is, i would probably look at them like a deer in headlights.
但如果有人让我解释为什么会这样,我可能会像头灯下的鹿一样看着它们。
So why/when is casting bad?
那么为什么/何时铸造不好?
Is it general for java, c#, c++ or does every different runtime environment deal with it on it's own terms?
它是通用的 java、c#、c++ 还是每个不同的运行时环境都按照自己的方式处理它?
Specifics for a any language are welcome, example why is it bad in c++?
欢迎提供任何语言的细节,例如为什么它在 c++ 中不好?
回答by Jerry Coffin
You've tagged this with three languages, and the answers are really quite different between the three. Discussion of C++ more or less implies discussion of C casts as well, and that gives (more or less) a fourth answer.
你用三种语言标记了这个,这三种语言之间的答案真的很不一样。对 C++ 的讨论或多或少也意味着对 C 强制转换的讨论,这给出了(或多或少)第四个答案。
Since it's the one you didn't mention explicitly, I'll start with C. C casts have a number of problems. One is that they can do any of a number of different things. In some cases, the cast does nothing more than tell the compiler (in essence): "shut up, I know what I'm doing" -- i.e., it ensures that even when you do a conversion that could cause problems, the compiler won't warn you about those potential problems. Just for example, char a=(char)123456;
. The exact result of this implementation defined (depends on the size and signedness of char
), and except in rather strange situations, probably isn't useful. C casts also vary in whether they're something that happens only at compile time (i.e., you're just telling the compiler how to interpret/treat some data) or something that happens at run time (e.g., an actual conversion from double to long).
由于它是您没有明确提及的那个,我将从 C 开始。 C 强制转换有很多问题。一个是他们可以做许多不同的事情。在某些情况下,强制转换无非是告诉编译器(本质上):“闭嘴,我知道我在做什么”——也就是说,它确保即使您进行可能导致问题的转换,编译器不会警告您这些潜在问题。举个例子,char a=(char)123456;
。定义的此实现的确切结果(取决于大小和符号char
),除非在相当奇怪的情况下,否则可能没有用。C 类型转换也因它们是仅在编译时发生的事情(即,您只是告诉编译器如何解释/处理某些数据)还是在运行时发生的事情(例如,从 double 到长)。
C++ attempts to deal with that to at least some extent by adding a number of "new" cast operators, each of which is restricted to only a subset of the capabilities of a C cast. This makes it more difficult to (for example) accidentally do a conversion you really didn't intend -- if you onlyintend to cast away constness on an object, you can use const_cast
, and be sure that the onlything it can affect is whether an object is const
, volatile
, or not. Conversely, a static_cast
is not allowed to affect whether an object is const
or volatile
. In short, you have most of the same types of capabilities, but they're categorized so one cast can generally only do one kind of conversion, where a single C-style cast can do two or three conversions in one operation. The primary exception is that you canuse a dynamic_cast
in place of a static_cast
in at least some cases and despite being written as a dynamic_cast
, it'll really end up as a static_cast
. For example, you can use dynamic_cast
to traverse up or down a class hierarchy -- but a cast "up" the hierarchy is always safe, so it can be done statically, while a cast "down" the hierarchy isn't necessarily safe so it's done dynamically.
C++ 试图通过添加许多“新”强制转换运算符来至少在某种程度上解决这个问题,每个运算符都仅限于 C 强制转换功能的一个子集。这使得(例如)意外地进行您真正不打算进行的转换变得更加困难——如果您只想抛弃对象上的常量,则可以使用const_cast
,并确保它唯一能影响的是一个目的是const
,volatile
或不是。相反,static_cast
不允许a影响对象是否是const
或volatile
. 简而言之,您拥有大多数相同类型的功能,但它们被归类,因此一个强制转换通常只能执行一种转换,而单个 C 样式强制转换可以在一次操作中执行两个或三个转换。主要的例外是至少在某些情况下您可以使用 adynamic_cast
代替 astatic_cast
并且尽管被编写为 a dynamic_cast
,但它实际上最终会成为 a static_cast
。例如,您可以使用dynamic_cast
向上或向下遍历类层次结构——但是“向上”层次结构的转换始终是安全的,因此可以静态完成,而“向下”层次结构的转换不一定安全,因此它是动态完成。
Java and C# are much more similar to each other. In particular, with both of them casting is (virtually?) always a run-time operation. In terms of the C++ cast operators, it's usually closest to a dynamic_cast
in terms of what's really done -- i.e., when you attempt to cast an object to some target type, the compiler inserts a run-time check to see whether that conversion is allowed, and throw an exception if it's not. The exact details (e.g., the name used for the "bad cast" exception) varies, but the basic principle remains mostly similar (though, if memory serves, Java does make casts applied to the few non-object types like int
much closer to C casts -- but these types are used rarely enough that 1) I don't remember that for sure, and 2) even if it's true, it doesn't matter much anyway).
Java 和 C# 彼此更相似。特别是,对于它们两者的转换(实际上?)总是一个运行时操作。就 C++ 转换运算符而言,它通常最接近 adynamic_cast
的实际操作——即,当您尝试将对象转换为某个目标类型时,编译器会插入运行时检查以查看是否允许该转换, 否则抛出异常。确切的细节(例如,用于“bad cast”异常的名称)各不相同,但基本原理大体相似(不过,如果没记错的话,Java 确实将强制转换应用于少数非对象类型,例如int
更接近 C强制转换——但这些类型的使用很少,以至于 1) 我肯定不记得了,和 2) 即使它是真的,也没什么大不了的)。
Looking at things more generally, the situation's pretty simple (at least IMO): a cast (obviously enough) means you're converting something from one type to another. When/if you do that, it raises the question "Why?" If you really want something to be a particular type, why didn't you define it to be that type to start with? That's not to say there's nevera reason to do such a conversion, but anytime it happens, it should prompt the question of whether you could re-design the code so the correct type was used throughout. Even seemingly innocuous conversions (e.g., between integer and floating point) should be examined much more closely than is common. Despite their seemingsimilarity, integers should really be used for "counted" types of things and floating point for "measured" kinds of things. Ignoring the distinction is what leads to some of the crazy statements like "the average American family has 1.8 children." Even though we can all see how that happens, the fact is that nofamily has 1.8 children. They might have 1 or they might 2 or they might have more than that -- but never 1.8.
从更一般的角度来看,情况非常简单(至少 IMO):强制转换(显然足够了)意味着您正在将某些类型从一种类型转换为另一种类型。当/如果你这样做,它提出了“为什么?”的问题。如果您真的希望某物成为特定类型,为什么不一开始就将其定义为该类型?这并不是说永远没有理由进行这样的转换,但是无论何时发生,它都会提示您是否可以重新设计代码以便始终使用正确的类型的问题。即使是看似无害的转换(例如,整数和浮点之间的转换)也应该比常见的更仔细地检查。尽管他们的外表相似性,整数应该真正用于“计数”类型的事物,而浮点数应该用于“测量”类型的事物。忽略这种区别会导致一些疯狂的陈述,例如“美国家庭平均有 1.8 个孩子”。尽管我们都知道这是如何发生的,但事实是没有一个家庭有 1.8 个孩子。他们可能有 1 个,也可能有 2 个,也可能有更多——但绝不会是 1.8 个。
回答by Eric Lippert
Lots of good answers here. Here's the way I look at it (from a C# perspective).
这里有很多很好的答案。这是我看待它的方式(从 C# 的角度)。
Casting usually means one of two things:
铸造通常意味着两件事之一:
I know the runtime type of this expression but the compiler does not know it. Compiler, I am telling you, at runtime the object that corresponds to this expression is really going to be of this type. As of now, you know that this expression is to be treated as being of this type. Generate code that assumes that the object will be of the given type, or, throw an exception if I'm wrong.
Both the compiler and the developer know the runtime type of the expression. There is another value of a different type associated with the value that this expression will have at runtime. Generate code that produces the value of the desired type from the value of the given type; if you cannot do so, then throw an exception.
我知道这个表达式的运行时类型,但编译器不知道。编译器,我告诉你,在运行时,对应于这个表达式的对象真的会是这种类型。到目前为止,您知道这个表达式将被视为属于这种类型。生成假定对象为给定类型的代码,或者,如果我错了,则抛出异常。
编译器和开发人员都知道表达式的运行时类型。有另一个不同类型的值与此表达式在运行时将具有的值相关联。生成从给定类型的值产生所需类型值的代码;如果您不能这样做,则抛出异常。
Notice that those are opposites. There are two kinds of casts! There are casts where you are giving a hintto the compiler about reality- hey, this thing of type object is actually of type Customer - and there are casts where you are telling the compiler to perform a mapping from one type to another- hey, I need the int that corresponds to this double.
请注意,这些是对立的。有两种类型的演员表!在某些情况下,您向编译器提供有关现实的提示- 嘿,对象类型实际上是 Customer 类型 - 在某些情况下,您告诉编译器执行从一种类型到另一种类型的映射- 嘿,我需要与此双精度对应的 int。
Both kinds of casts are red flags. The first kind of cast raises the question "why exactly is it that the developer knows something that the compiler doesn't?" If you are in that situation then the better thing to do is usually to change the program so that the compiler doeshave a handle on reality. Then you don't need the cast; the analysis is done at compile time.
两种类型的演员表都是危险信号。第一种类型转换提出了一个问题“为什么开发人员知道编译器不知道的东西?” 如果您处于这种情况,那么最好的办法通常是更改程序,以便编译器确实能够处理现实情况。那你就不需要演员表了;分析是在编译时完成的。
The second kind of cast raises the question "why isn't the operation being done in the target data type in the first place?" If you need a result in ints then why are you holding a double in the first place? Shouldn't you be holding an int?
第二种类型转换提出了一个问题“为什么不首先在目标数据类型中进行操作?” 如果你需要一个整数结果,那么为什么你首先要持有一个双精度数?你不应该持有一个int吗?
Some additional thoughts here:
这里有一些额外的想法:
http://blogs.msdn.com/b/ericlippert/archive/tags/cast+operator/
http://blogs.msdn.com/b/ericlippert/archive/tags/cast+operator/
回答by Mike Miller
Casting errors are always reported as run-time errors in java. Using generics or templating turns these errors into compile-time errors, making it much easier to detect when you have made a mistake.
强制转换错误总是在 java 中报告为运行时错误。使用泛型或模板会将这些错误转化为编译时错误,从而在您犯了错误时更容易检测到。
As I said above. This isn't to say that all casting is bad. But if it is possible to avoid it, its best to do so.
正如我上面所说的。这并不是说所有演员都不好。但如果有可能避免它,最好这样做。
回答by Steve Townsend
Casting is not inherently bad, it's just that it's often misused as a means to achieve something that really should either not be done at all, or done more elegantly.
铸造本身并不坏,只是它经常被误用为实现某些根本不应该做的事情,或者做得更优雅的手段。
If it was universally bad, languages would not support it. Like any other language feature, it has its place.
如果它普遍不好,语言将不支持它。像任何其他语言功能一样,它也有它的位置。
My advice would be to focus on your primary language, and understand all its casts, and associated best practices. That should inform excursions into other languages.
我的建议是专注于您的主要语言,并了解其所有类型以及相关的最佳实践。这应该会告知其他语言的短途旅行。
The relevant C# docs are here.
相关的 C# 文档在这里。
There is a great summary on C++ options at a previous SO question here.
在上一个 SO question here 中有关于 C++ 选项的很好的总结。
回答by sbi
I'm mostly speaking for C++here, but most of this probably applies to Java and C# as well:
我在这里主要说的是C++,但其中大部分内容可能也适用于 Java 和 C#:
C++ is a statically typed language. There are some leeways the language allows you in this (virtual functions, implicit conversions), but basically the compiler knows the type of every object at compile-time. The reason to use such a language is that errors can be caught at compile-time. If the compiler know the types of a
and b
, then it will catch you at compile-time when you do a=b
where a
is a complex number and b
is a string.
C++ 是一种静态类型语言。语言允许你在这方面有一些回旋余地(虚拟函数、隐式转换),但基本上编译器在编译时知道每个对象的类型。使用这种语言的原因是可以在编译时捕获错误。如果编译器知道a
and的类型b
,那么当您执行a=b
wherea
是复数并且b
是字符串时,它会在编译时捕获您。
Whenever you do explicit casting you tell the compiler to shut up, because you think you know better. In case you're wrong, you will usually only find out at run-time. And the problem with finding out at run-time is, that this might be at a customer's.
每当您进行显式转换时,您都会告诉编译器闭嘴,因为您认为您更了解. 如果你错了,你通常只会在运行时发现。在运行时发现的问题是,这可能是客户的。
回答by Kdeveloper
Java, c# and c++ are strongly typed languages, although strongly typed languages can be seen as inflexible, they have the benefit of doing type checking at compile time and protect you against runtime errors that are caused by having the wrong type for certain operations.
Java、c# 和 c++ 是强类型语言,虽然强类型语言可能被视为不灵活,但它们具有在编译时进行类型检查的好处,并保护您免受由于某些操作的类型错误而导致的运行时错误。
There are basicaly two kind of casts: a cast to a more general type or a cast to an other types (more specific). Casting to a more general type (casting to a parent type) will leave the compile time checks intact. But casting to other types (more specific types) will disable compile time type checking and will be replaced by the compiler by a runtime check. This means you have less certainty you're compiled code will run correctly. It also has some negligible performance impact, due to the extra runtime type check (the Java API is full of casts!).
基本上有两种类型转换:转换为更一般的类型或转换为其他类型(更具体)。转换为更通用的类型(转换为父类型)将使编译时检查保持不变。但是转换为其他类型(更具体的类型)将禁用编译时类型检查,并将被编译器替换为运行时检查。这意味着您不太确定您编译的代码会正确运行。由于额外的运行时类型检查(Java API 充满了强制转换!),它也有一些可以忽略不计的性能影响。
回答by Jon Hanna
Some types of casting are so safe and efficient as to often not even be considered casting at all.
某些类型的铸造非常安全和有效,以至于通常根本不考虑铸造。
If you cast from a derived type to a base type, this is generally quite cheap (often - depending on language, implementation and other factors - it is zero-cost) and is safe.
如果您从派生类型转换为基类型,这通常非常便宜(通常 - 取决于语言、实现和其他因素 - 它是零成本)并且是安全的。
If you cast from a simple type like an int to a wider type like a long int, then again it is often quite cheap (generally not much more expensive than assigning the same type as that cast to) and again is safe.
如果您从简单的类型(如 int)转换为更宽的类型(如 long int),那么它通常非常便宜(通常不会比分配与该转换相同的类型昂贵多少)并且再次是安全的。
Other types are more fraught and/or more expensive. In most languages casting from a base type to a derived type is either cheap but has a high risk of severe error (in C++ if you static_cast from base to derived it will be cheap, but if the underlying value is not of the derived type the behaviour is undefined and can be very strange) or relatively expensive and with a risk of raising an exception (dynamic_cast in C++, explicit base-to-derived cast in C#, and so on). Boxing in Java and C# is another example of this, and an even greater expense (considering that they are changing more than just how the underlying values are treated).
其他类型更令人担忧和/或更昂贵。在大多数语言中,从基类型到派生类型的转换既便宜,又会带来严重错误的高风险(在 C++ 中,如果您从基类型静态转换到派生,它将很便宜,但如果基础值不是派生类型,则行为未定义并且可能非常奇怪)或者相对昂贵并且有引发异常的风险(C++ 中的 dynamic_cast,C# 中显式的从基到派生的强制转换,等等)。Java 和 C# 中的装箱是另一个例子,而且开销更大(考虑到它们正在改变的不仅仅是如何处理底层值)。
Other types of cast can lose information (a long integer type to a short integer type).
其他类型的转换可能会丢失信息(长整数类型到短整数类型)。
These cases of risk (whether of exception or a more serious error) and of expense are all reasons to avoid casting.
这些风险情况(无论是异常还是更严重的错误)和费用都是避免强制转换的原因。
A more conceptual, but perhaps more important, reason is that each case of casting is a case where your ability to reason about the correctness of your code is stymied: Each case is another place where something can go wrong, and the ways in which it can go wrong add to the complexity of deducing whether the system as a whole will go wrong. Even if the cast is provably safe each time, proving this is an extra part of the reasoning.
一个更具概念性但可能更重要的原因是,每种类型的强制转换都阻碍了您对代码正确性进行推理的能力:每种情况都是另一个可能出错的地方,以及出错的方式可能会出错增加了推断整个系统是否会出错的复杂性。即使每次都可以证明演员表是安全的,证明这是推理的额外部分。
Finally, the heavy use of casts can indicate a failure to consider the object model well either in creating it, using it, or both: Casting back and forth between the same few types frequently is almost always a failure to consider the relationships between the types used. Here it's not so much that casts are bad, as they are a sign of something bad.
最后,大量使用强制转换可能表明在创建、使用对象模型时未能很好地考虑对象模型,或两者兼而有之:在相同的少数类型之间频繁来回转换几乎总是未能考虑类型之间的关系用过的。这里并不是说演员阵容不好,因为它们是坏事的标志。
回答by user168715
There is a growing tendency for programmers to cling to dogmatic rules about use of language features ("never use XXX!", "XXX considered harmful", etc), where XXX ranges from goto
s to pointers to protected
data members to singletons to passing objects by value.
程序员越来越倾向于坚持使用语言特性的教条规则(“永远不要使用 XXX!”、“XXX 被认为是有害的”等),其中 XXX 的范围从goto
s 到指向protected
数据成员的指针到单例到传递对象价值。
Following such rules, in my experience, ensures two things: you will not be a terrible programmer, nor will you be a great programmer.
根据我的经验,遵循这些规则可以确保两件事:你不会成为一个糟糕的程序员,也不会成为一个伟大的程序员。
A much better approach is to dig down and uncover the kernel of truth behind these blanket prohibitions, and then use the features judiciously, with the understanding that there aremany situations for which they're the best tool for the job.
更好的方法是挖掘并揭示这些全面禁止背后的真相,然后明智地使用这些功能,并了解在许多情况下它们是完成工作的最佳工具。
"I generally avoid casting types as much as possible" is a good example of such an overgeneralized rule. Casts are essential in many common situations. Some examples:
“我通常尽可能避免强制转换类型”是这种过度概括规则的一个很好的例子。在许多常见情况下,演员表是必不可少的。一些例子:
- When interoperating with third-party code (especially when that code is rife with
typedef
s). (Example:GLfloat
<-->double
<-->Real
.) - Casting from a derived to base class pointer/reference: This is so common and natural that the compiler will do it implicitly. If making it explicit increases readability, the cast is a step forwards, not backwards!
- Casting from a base to derived class pointer/reference: Also common, even in well-designed code. (Example: heterogeneous containers.)
- Inside binary serialization/deserialization or other low-level code that needs access to the raw bytes of built-in types.
- Any time when it's just plain more natural, convenient, and readable to use a different type. (Example:
std::size_type
-->int
.)
- 与第三方代码互操作时(尤其是当该代码充斥着
typedef
s 时)。(例如:GLfloat
<-->double
<-->Real
。) - 从派生类指针/引用转换为基类指针/引用:这是非常常见和自然的,编译器会隐式执行。如果使其明确增加可读性,则演员表是向前迈进了一步,而不是向后!
- 从基类转换为派生类指针/引用:即使在设计良好的代码中也很常见。(例如:异构容器。)
- 在二进制序列化/反序列化或其他需要访问内置类型原始字节的低级代码中。
- 任何时候使用不同的类型都更自然、更方便、更易读。(例如:
std::size_type
-->int
.)
There are certainly many situations where it's notappropriate to use a cast, and it's important to learn these as well; I won't go into too much detail since the answers above have done a good job pointing some of them out.
肯定有很多情况不适合使用演员表,学习这些也很重要;我不会详细介绍,因为上面的答案已经很好地指出了其中的一些。
回答by user472875
Not sure if someone already mentioned this, but in C# casting can be used in a rather safe manner, and is often necessary. Suppose you receive an object which can be of several types. Using the is
keyword you can first confirm that the object is indeed of the type you are about to cast it to, and then cast the object to that type directly. (I didn't work with Java much but I'm sure there's a very straightforward way of doing it there as well).
不确定是否有人已经提到过这一点,但在 C# 中可以以一种相当安全的方式使用强制转换,并且通常是必要的。假设您收到一个可以是多种类型的对象。使用is
关键字,您可以首先确认对象确实是您将要转换为的类型,然后直接将对象转换为该类型。(我很少使用 Java,但我确信在那里也有一种非常简单的方法)。
回答by back2dos
You only cast an object to some type, if 2 conditions are met:
如果满足 2 个条件,您只能将对象强制转换为某种类型:
- you know it is of that type
- the compiler doesn't
- 你知道它是那种类型的
- 编译器没有
This means not all the information you have is well represented in the type structure you use. This is bad, because your implementation should semanticallycomprise your model, which it clearly doesn't in this case.
这意味着并非您拥有的所有信息都在您使用的类型结构中得到了很好的表示。这很糟糕,因为您的实现应该在语义上包含您的模型,而在这种情况下显然不是。
Now when you do a cast, then this can have 2 different reasons:
现在,当您进行演员表时,这可能有两个不同的原因:
- You did a bad job in expressing the type relationships.
- the languages type system simply is not expressive enough to phrase them.
- 你在表达类型关系方面做得不好。
- 语言类型系统的表达能力不足以表达它们。
In most languages you run into the 2nd situation a lot of times. Generics as in Java help a bit, the C++ template system even more, but it is hard to master and even then some things may be impossible or just not worth the effort.
在大多数语言中,您会多次遇到第二种情况。Java 中的泛型有点帮助,C++ 模板系统更有用,但很难掌握,即使如此,有些事情可能是不可能的,或者只是不值得付出努力。
So you could say, a cast is a dirty hack to circumvent your problems to express some specific type relationship in some specific language. Dirty hacks should be avoided. But you can never live without them.
所以你可以说,转换是一种肮脏的技巧,可以规避你的问题,用某种特定的语言表达某种特定的类型关系。应该避免脏黑客。但没有它们,你永远无法生活。