类转换错误:Java 7与Java 8的区别

30
这是一个bug还是特性?以下代码在Java 7中可以正常运行,但在Java 8中会抛出异常:
在Java 8中,最后一条命令会抛出ClassCast异常,所有上面的“等效”命令都以相同的方式工作。
我认为问题在于Java 8中编译器决定在最后一行上使用String.value(char[])而不是像Java 7中那样使用String.value(Object)。我认为这应该为了向后兼容而表现相同。我错过了什么吗?
注意:正如Marko所建议的,这可能与Java 8中引入的目标类型推断有关。
public class Test {
    public static void main(String[] args) {
        System.out.println( getVal().getClass());  // String

        System.out.println( String.valueOf(Test.<Object>getVal()) );   // "abc"

        Object obj = getVal();
        System.out.println( String.valueOf(obj) );  // "abc"

        System.out.println( String.valueOf(getVal()) ); // 7: "abc", 8: Exception 
    }

    // returns a string for simplicity; imagine that given a field, it fetches values from a database
    @SuppressWarnings("unchecked")
    public static <T> T getVal() {
        return (T) "abc";
    }
}

Java 7中的结果:

class java.lang.String
abc
abc
abc

Java 8的结果:

class java.lang.String
abc
abc
Exception in thread "main" java.lang.ClassCastException: java.lang.String cannot be cast to [C
    at Test.main(Test.java:11)

(注:[C是一个字符数组)
两个Java版本都在Windows上:
java version "1.7.0_45"
Java(TM) SE Runtime Environment (build 1.7.0_45-b18)
Java HotSpot(TM) Client VM (build 24.45-b08, mixed mode, sharing)

java version "1.8.0_05"
Java(TM) SE Runtime Environment (build 1.8.0_05-b13)
Java HotSpot(TM) 64-Bit Server VM (build 25.5-b02, mixed mode)

对我来说似乎是个bug。 - RaceBase
无法转换为 [C,您在复制/粘贴时是否遗漏了什么? - Rogue
1
@Rogue:在我看来,这似乎是字符数组的符号表示法。 - user2357112
1个回答

23

String.valueOf 是一个重载方法,您正在使用它的上下文需要从上下文推断参数类型。然而,在Java 8中,类型推断规则进行了重大改革,其中最值得注意的是目标类型推断得到了很大的改进。因此,在Java 8之前,该方法的参数位置没有接收到任何推断,默认情况下为Object;但在Java 8中,将推断出最具体适用的类型,本例中为char[]

然而,请记住,在两种情况下,您使用的习惯用法基本上是错误的,因此编译器输出结果的更改应该被视为“陷阱”,而不是“错误”。

Unchecked cast(未经检查的转换)有时是不可避免的,但我想不出任何情况下可以推断出类型本身(而不是类型参数)的东西,这些东西并非通过Class对象进行反射创建。因此,您不太可能真正发现自己处于这里所示的位置,即根据调用点上可接受的参数类型推断类型。此外,对于重载方法来说,留给推断选择参数类型只能“碰巧”起作用,这显然是错误的。


2
两种推断都不像是显而易见的“正确答案”,所以我不会急于将其标记为错误。如果旧代码依赖这种偶然的推断,那么它应该被修复。 - Marko Topolnik
5
同意。在我看来,这里唯一的问题是 (T) "abc" - Paul Bellora
3
第二种情况受到目标类型推断的影响,在Java 7中不存在这种情况。这可能是一个线索,而char[]是在该位置上合法的“最具体的类型”。 - Marko Topolnik
1
@SotiriosDelimanolis 我认为在Java 7中,类型推断只能在赋值语句中起作用,其中目标类型是明显的。 - Marko Topolnik
3
这就是为什么我认为没有什么必要抱怨Java 7和8之间编译器输出的变化。编译器只是向你展示了 未经检查的强制类型转换 警告一直存在的原因。请注意,警告信息是有充分理由的。 - Marko Topolnik
显示剩余21条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接