Ошибка ClassCast: Java 7 против Java 8

Это ошибка или функция? Следующий код отлично работает в Java 7, но генерирует исключение в Java 8:

Последняя команда генерирует исключение ClassCast в Java8, все вышеперечисленные команды эквивалентны таким же образом.

Проблема, я думаю, в том, что в Java 8 компилятор решает использовать String.value(char[]) в последней строке вместо String.value(Object), как в Java 7. Я бы подумал, что это должно вести себя одинаково для обратной совместимости. Я что-то пропустил?

Примечание. Поскольку Марко предположил, что это, вероятно, связано с выводом типа цели, представленным на 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 находятся на окнах:

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)

Ответ 1

String.valueOf - сильно перегруженный метод, и вы используете его в контексте, где тип аргумента должен быть выведен из контекста. С другой стороны, правила вывода типа получили существенный пересмотр в Java 8; наиболее заметно, что вывод типа цели значительно улучшился. Итак, в то время как перед Java 8 сайт аргумента метода не получил никакого вывода, по умолчанию в Object в вашем случае, в Java 8 был выведен наиболее конкретный применимый тип, в данном случае char[].

Однако имейте в виду, что в обоих случаях используемая вами идиома по существу нарушается, поэтому изменение в выходе компилятора должно быть, возможно, обозначаться как "ловушка", но не "ошибка".

Непринужденный актер, к сожалению, иногда неизбежен, но я не могу придумать ни одного случая, когда имеет смысл вывести сам тип (в отличие от параметра типа) того, что не создается рефлексивно от объекта Class, Поэтому вы вряд ли сможете оказаться в позиции, показанной здесь, где вы выберете тип, основанный на допустимых типах аргументов на сайте вызова. Более того, это, безусловно, нарушено для этого с помощью перегруженного метода, оставляя выбор типа аргумента для вывода. Это может работать только "случайно".