类型擦除后何时强制转换函数的通用返回值?

Tamas Hegedus:

这个问题是由有关不安全强制转换的StackOverflow问题引起的:Java强制转换方法不知道要强制转换为什么在回答我遇到的问题时,我无法仅根据规范进行解释

我在Oracle文档的The Java Tutorials中找到了以下语句:

没有解释“必要时”的确切含义,并且Java语言规范我完全没有提到这些强制转换,因此我开始尝试。

让我们看下面的代码:

// 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'
}

使用Java Decompiler进行编译javac和反编译

// 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());

这意味着,如果一个泛型函数应该返回给定类型的对象,也不能保证它最终返回类型。使用上述代码的应用程序在尝试将返回值强制转换为a的第一点将完全失败Integer,因此我觉得它违反了快速失败原则

编译器在编译期间插入此强制转换以确保类型安全的确切规则是什么?这些规则在哪里指定?

编辑:

我看到编译器不会深入研究代码,而是试图证明通用代码确实返回了应有的结果,但是它可以插入一个断言,或者至少插入一个类型强制转换(在特定情况下,它已经这样做了,如第一个示例)以确保返回类型正确,因此后者将抛出ClassCastException

// It could compile to this, throwing ClassCastException:
Object localObject2 = (Integer)unsafeIdentity("foo");
newacct:

如果您在规范中找不到它,则表示未指定它,并且只要擦除的代码满足非泛型代码的类型安全规则,则由编译器实现决定是否在哪里插入强制类型转换。 。

在这种情况下,编译器的擦除代码如下所示:

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该可更改类型,因此此处需要进行运行时检查。

在第二种情况下,擦除的代码无需强制转换即可编译。是的,如果添加了强制转换,它也会编译。因此,编译器可以选择任何一种方式。在这种情况下,编译器决定不插入强制类型转换。那是一个完全有效的选择。您不应该依赖编译器来决定哪种方式。

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章

强制转换返回强制类型的函数的返回值

当具有有界类型参数的类的方法返回其自身类型的值时,如何让返回值保持通用(不进行强制转换)?

在带有Swift的函数中使用类型擦除返回通用类型(无法转换类型的返回表达式…)

类型转换C函数的返回值是否可以接受?

函数类似于“何时”,但返回值?

C ++函数何时删除返回值?

没有从“int”类型的返回值到函数返回类型(向量)的可行转换

没有从 bankAccount 类型的返回值到函数返回类型 int 的可行转换

不兼容的指针类型和返回值使整数无强制转换问题

如何在通用类的返回值中强制键入?

如何在通用类的返回值中强制键入?

Java的通用双函数返回值基于指定类的类型

Scala强制转换为通用类型(用于通用数值函数)

C - 将函数调用返回值强制转换为 void

返回值的隐式类型转换

Javascript函数:拆分后返回值

返回值的复制构造函数何时发生

Coq-类型的返回值等于函数的返回类型

通用方法实现中的不同返回值类型

打字稿:keyof返回值的通用类型限制

Swift中的通用类型作为返回值

如何解组用作方法返回值的通用类型?

当我使用void函数的返回值(通过强制转换函数指针)时,会发生什么?

如何避免使用通用返回值进行转换?

为什么没有 else 块会转换为函数的 Unit 类型返回值?

在Go中,是否可以对一个函数的多个返回值执行类型转换?

eth合约函数何时需要带存储的返回值?为什么不只使用内存类型?

Kotlin函数返回值的默认通用参数

强制执行通用类型返回函数