用Java转换变量


84

我想知道是否有人可以告诉我铸造的工作原理?我知道何时应该执行此操作,但并不真正了解它的工作原理。对于原始数据类型,我会部分理解,但是在涉及对象转换时,我并不了解它是如何工作的。

例如,如何突然将类型为Object的对象转换为对象MyType(仅作为示例),然后获取所有方法?



Answers:


182

用Java进行强制转换不是魔术,而是告诉编译器A类型的Object实际上是更特定的B类型,因此可以访问B上所有其他方法。在执行强制转换时,您没有执行任何魔术或转换操作,实际上是在告诉编译器“相信我,我知道我在做什么,并且我可以向您保证此行上的此Object实际上是<Insert cast在这里输入>。” 例如:

Object o = "str";
String str = (String)o;

以上很好,不是魔术,一切都很好。存储在o中的对象实际上是一个字符串,因此我们可以将其转换为字符串而不会出现任何问题。

有两种方法可能会出错。首先,如果要在完全不同的继承层次结构中的两种类型之间进行转换,则编译器将知道您是愚蠢的并阻止您:

String o = "str";
Integer str = (Integer)o; //Compilation fails here

其次,如果它们在相同的层次结构中但仍然是无效的强制转换,则ClassCastException在运行时将抛出a :

Number o = new Integer(5);
Double n = (Double)o; //ClassCastException thrown here

这实质上意味着您已经违反了编译器的信任。您已经告诉您可以保证对象是特定类型的,但事实并非如此。

为什么需要铸造?好吧,首先,只有在从更一般的类型变为更具体的类型时才需要它。例如,Integer继承自Number,因此,如果您要存储一个Integeras,Number那么就可以了(因为所有的Integers都是Numbers。)但是,如果要沿相反的方向进行转换,则需要强制转换-并非所有Numbers都是Integers(同样作为整数我们有DoubleFloatByteLong等),即使在你的项目或JDK只是一个子类,有人可以很容易地创建另一个和分发,所以,你不能保证,即使你认为这是一个单一的,显而易见的选择!

关于强制转换,您仍然可以在某些库中看到对它的需求。在Java-5之前的版本中,它在集合和各种其他类中大量使用,因为所有集合都工作在添加对象,然后转换结果以使您退出集合。但是,随着泛型的出现,铸造的许多用途已不复存在-它已被泛型所取代,泛型提供了一种更安全的替代方法,而没有潜在的ClassCastExceptions(实际上,如果您干净地使用泛型并且编译时没有警告,您可以保证永远不会收到ClassCastException。)


谢谢你的解释。如果我理解这是正确的,可能不是,当您强制转换对象时,您只是在告诉编译器,我知道此内存地址上的对象知道如何响应这些方法,等等,所以不要否认我吗?那是对的吗?
user626912 2011年

1
@ user626912的种类-尽管不要在内存地址方面考虑它。您不仅仅是在告诉编译器给定的对象符合接口并因此具有给定方法的实现(您可以使用相同的方法创建完全不同的对象,并且强制转换不一定有效。)您在告诉编译器一种类型的对象实际上是一种更特定的类型,因此您可以使用该特定对象上可用的方法。如果您还没有读懂多态,那么可能会使某些事情变得更清楚。
Michael Berry

我怀疑您的声明“只有在从更一般的类型转换为更具体的类型时才需要它”。((Object)gpsLastLoc.getLatitude()。getClass()。getSimpleName()将在运行时返回“双”名称,这是从更具体的类型转换为更一般的类型的示例用法。
2016年

1
@林果皞在这种情况下,您只是使用强制转换来提升基本类型-这与使用更通用的类型不同,这是一种非常奇怪的处理方式。更为正常(更好)的方式将是Double.valueOf(gpsLastLoc.getLatitude()).getClass().getSimpleName()。在这两种情况下,您都不需要动态获取基元的类,因为如果getLatitude()返回一个双基元,您始终知道它将提升为Double对象。
Michael Berry

我喜欢您这样说,即代码“违反了编译器的信任”。(对,您有错字,您写的是“ you're”而不是“ you've”。)
Jason L.

7

实际上,强制转换并不总是有效。如果对象不是instanceof您要投射到的类,则ClassCastException在运行时将得到一个。


5

假设您想将a强制转换String为a File(是的,没有任何意义),则不能直接对其进行强制转换,因为File该类不是该对象的子级,也不是其父级String级(并且编译器抱怨)。

但是您可以将您强制转换StringObject,因为aStringObjectObject是父级)。然后,您可以将此对象转换为File,因为File是Object

所以从编译的角度来看,所有操作都是合法的,但这并不意味着它将在运行时起作用!

File f = (File)(Object) "Stupid cast";

即使没有意义,编译器也会允许这样做,但是会在运行时因以下异常而崩溃:

Exception in thread "main" java.lang.ClassCastException:
    java.lang.String cannot be cast to java.io.File

3

仅当引用instanceof类型时,该类型才会起作用。您不能投放随机引用。另外,您需要阅读有关的更多信息Casting Objects

例如

String string = "String";

Object object = string; // Perfectly fine since String is an Object

String newString = (String)object; // This only works because the `reference` object is pointing to a valid String object.

3

正确的方法是这样的:

Integer i = Integer.class.cast(obj);

该方法cast()是编译时强制转换的更安全的替代方法。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.