- Java 双重比较
- java - 比较器与 Apache BeanComparator
- Objective-C 完成 block 导致额外的方法调用?
- database - RESTful URI 是否应该公开数据库主键?
这个问题是由 this StackOverflow question about unsafe casts: Java Casting method without knowing what to cast to 提出的.在回答问题时,我遇到了这种行为,我无法仅根据规范进行解释
我在 The Java Tutorials 找到了下面的语句Oracle 文档:
- Insert type casts if necessary to preserve type safety. The Java Tutorials: Type Erasure
没有解释“如有必要”的确切含义,并且我在 Java LanguageSpecification 中发现没有提及这些类型转换完全没有,所以我开始尝试。
让我们看下面这段代码:
// Java source
public static <T> T identity(T x) {
return x;
}
public static void main(String args[]) {
String a = identity("foo");
System.out.println(a.getClass().getName());
// Prints 'java.lang.String'
Object b = identity("foo");
System.out.println(b.getClass().getName());
// Prints 'java.lang.String'
}
用javac
编译,用the Java Decompiler反编译:
// Decompiled code
public static void main(String[] paramArrayOfString)
{
// The compiler inserted a cast to String to ensure type safety
String str = (String)identity("foo");
System.out.println(str.getClass().getName());
// The compiler omitted the cast, as it is not needed
// in terms of runtime type safety, but it actually could
// do an additional check. Is it some kind of optimization
// to decrease overhead? Where is this behaviour specified?
Object localObject1 = identity("foo");
System.out.println(localObject1.getClass().getName());
}
我可以看到在第一种情况下有一个确保类型安全的转换,但在第二种情况下,它被省略了。这是当然可以,因为我想将返回值存储在 Object
中类型变量,因此根据类型安全性,强制转换不是绝对必要的。然而,它会导致不安全转换的有趣行为:
public class Erasure {
public static <T> T unsafeIdentity(Object x) {
return (T) x;
}
public static void main(String args[]) {
// I would expect c to be either an Integer after this
// call, or a ClassCastException to be thrown when the
// return value is not Integer
Object c = Erasure.<Integer>unsafeIdentity("foo");
System.out.println(c.getClass().getName());
// but Prints 'java.lang.String'
}
}
编译和反编译后,我没有看到类型转换以确保在运行时返回正确的类型:
// The type of the return value of unsafeIdentity is not checked,
// just as in the second example.
Object localObject2 = unsafeIdentity("foo");
System.out.println(localObject2.getClass().getName());
这意味着如果一个通用函数应该返回一个给定的对象类型,不能保证它将最终返回该类型。一个使用上述代码的应用程序将在它尝试的第一点失败将返回值转换为 Integer
如果它确实这样做了,所以我觉得它打破了 fail-fast principle .
编译器插入此强制转换的确切规则是什么确保类型安全的编译以及这些规则在哪里指定?
编辑:
我看到编译器不会深入研究代码并尝试证明通用代码确实返回了它应该返回的内容,但它可以插入一个断言,或者至少是一个类型转换(它已经在特定情况下这样做了,如第一个示例所示)以确保正确的返回类型,因此后者将抛出 ClassCastException
:
// It could compile to this, throwing ClassCastException:
Object localObject2 = (Integer)unsafeIdentity("foo");
最佳答案
如果你在规范中找不到它,那就意味着它没有被指定,并且由编译器实现来决定在哪里插入强制转换,只要被删除的代码满足非类型安全规则-通用代码。
在这种情况下,编译器删除的代码如下所示:
public static Object identity(Object x) {
return x;
}
public static void main(String args[]) {
String a = (String)identity("foo");
System.out.println(a.getClass().getName());
Object b = identity("foo");
System.out.println(b.getClass().getName());
}
在第一种情况下,在删除的代码中强制转换是必要的,因为如果删除它,删除的代码将无法编译。这是因为 Java 保证在运行时保存在可具体化类型的引用变量中的必须是 instanceOf
可具体化类型,所以这里需要进行运行时检查。
在第二种情况下,删除的代码无需转换即可编译。是的,如果您添加了类型转换,它也会编译。所以编译器可以决定任何一种方式。在这种情况下,编译器决定不插入强制转换。这是一个完全正确的选择。您不应该依赖编译器来决定任何一种方式。
关于java - 类型删除后何时转换函数的通用返回值?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34562435/
C语言sscanf()函数:从字符串中读取指定格式的数据 头文件: ?
最近,我有一个关于工作预评估的问题,即使查询了每个功能的工作原理,我也不知道如何解决。这是一个伪代码。 下面是一个名为foo()的函数,该函数将被传递一个值并返回一个值。如果将以下值传递给foo函数,
CStr 函数 返回表达式,该表达式已被转换为 String 子类型的 Variant。 CStr(expression) expression 参数是任意有效的表达式。 说明 通常,可以
CSng 函数 返回表达式,该表达式已被转换为 Single 子类型的 Variant。 CSng(expression) expression 参数是任意有效的表达式。 说明 通常,可
CreateObject 函数 创建并返回对 Automation 对象的引用。 CreateObject(servername.typename [, location]) 参数 serv
Cos 函数 返回某个角的余弦值。 Cos(number) number 参数可以是任何将某个角表示为弧度的有效数值表达式。 说明 Cos 函数取某个角并返回直角三角形两边的比值。此比值是
CLng 函数 返回表达式,此表达式已被转换为 Long 子类型的 Variant。 CLng(expression) expression 参数是任意有效的表达式。 说明 通常,您可以使
CInt 函数 返回表达式,此表达式已被转换为 Integer 子类型的 Variant。 CInt(expression) expression 参数是任意有效的表达式。 说明 通常,可
Chr 函数 返回与指定的 ANSI 字符代码相对应的字符。 Chr(charcode) charcode 参数是可以标识字符的数字。 说明 从 0 到 31 的数字表示标准的不可打印的
CDbl 函数 返回表达式,此表达式已被转换为 Double 子类型的 Variant。 CDbl(expression) expression 参数是任意有效的表达式。 说明 通常,您可
CDate 函数 返回表达式,此表达式已被转换为 Date 子类型的 Variant。 CDate(date) date 参数是任意有效的日期表达式。 说明 IsDate 函数用于判断 d
CCur 函数 返回表达式,此表达式已被转换为 Currency 子类型的 Variant。 CCur(expression) expression 参数是任意有效的表达式。 说明 通常,
CByte 函数 返回表达式,此表达式已被转换为 Byte 子类型的 Variant。 CByte(expression) expression 参数是任意有效的表达式。 说明 通常,可以
CBool 函数 返回表达式,此表达式已转换为 Boolean 子类型的 Variant。 CBool(expression) expression 是任意有效的表达式。 说明 如果 ex
Atn 函数 返回数值的反正切值。 Atn(number) number 参数可以是任意有效的数值表达式。 说明 Atn 函数计算直角三角形两个边的比值 (number) 并返回对应角的弧
Asc 函数 返回与字符串的第一个字母对应的 ANSI 字符代码。 Asc(string) string 参数是任意有效的字符串表达式。如果 string 参数未包含字符,则将发生运行时错误。
Array 函数 返回包含数组的 Variant。 Array(arglist) arglist 参数是赋给包含在 Variant 中的数组元素的值的列表(用逗号分隔)。如果没有指定此参数,则
Abs 函数 返回数字的绝对值。 Abs(number) number 参数可以是任意有效的数值表达式。如果 number 包含 Null,则返回 Null;如果是未初始化变量,则返回 0。
FormatPercent 函数 返回表达式,此表达式已被格式化为尾随有 % 符号的百分比(乘以 100 )。 FormatPercent(expression[,NumDigitsAfterD
FormatNumber 函数 返回表达式,此表达式已被格式化为数值。 FormatNumber( expression [,NumDigitsAfterDecimal [,Inc
我是一名优秀的程序员,十分优秀!