Является ли эта ошибка эзотерических генериков ошибкой компилятора или новым ограничением? (предполагаемый тип не соответствует верхним границам)

Я обновил с Java 8u5 до 8u45, а некоторые ранее работающие коды больше не компилируются. Проблема в том, что в половине случаев, когда это происходит, это было преднамеренное изменение, поэтому я не могу понять, является ли это ошибкой или нет.

(Я также тестировал до u25, и каждая версия делает то же самое, что и u45.)

Но, по существу, это связано с несколькими точками возврата от метода. например:.

import java.sql.Connection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;

public class CompilerIssue
{
    public Set<String> test(int value)
    {
        return perform(connection -> {
            if (value % 2 == 0)
            {
                return Collections.<String>emptySet();
            }
            else
            {
                return new HashSet<>(10);
            }
        });
    }

    <V> V perform(BusinessLogic<V> logic)
    {
        // would usually get a connection
        return null;
    }

    interface BusinessLogic<V>
    {
        V execute(Connection connection) throws Exception;
    }
}

javac дает:

Error:(12, 23) java: incompatible types: inferred type does not conform to upper bound(s)
    inferred: java.util.Set<? extends java.lang.Object>
    upper bound(s): java.util.Set<java.lang.String>,java.lang.Object

ИДЕЯ, как обычно с подобными вещами, не видит никакой проблемы.

Я уже знаю исправление - замените HashSet<> на HashSet<String>. Но мы использовали эту структуру повсеместно через наш код, поэтому, прежде чем тратить время на ее изменение, я хотел бы знать: это ошибка, или было старое поведение ошибки?

(Если это ошибка, тогда я должен сообщить об ошибке в Oracle. Если это функция, я должен сообщить об ошибке в IDEA, думая, что это нормально.)

Ответ 1

Насколько я вижу, это ошибка, и это от Oracle, она не выводит корректно тип из целевого типа (тип, который ожидает предложение return), вместо этого он как-то сравнивает верхнюю границу обоих предложений return: (return Collections.<String>emptySet(); и return new HashSet<>(10);).

Во втором возвратном выражении верхняя граница только new HashSet<>(10) будет Object, но тип может быть выведен из возвращаемого типа, который действительно <String>. Попробуйте так же, как тест, комментирующий первый оператор return и возвращающий null вместо этого и компилирующийся:

public class CompilerIssue
{
    public Set<String> test(int value)
    {
        return perform(connection -> {
            if (value % 2 == 0)
            {
                return null; // COMMENTED OUT FOR TESTING PURPOSES Collections.<String>emptySet();
            }
            else
            {
                return new HashSet<>(10);
            }
        });
    }

    <V> V perform(BusinessLogic<V> logic)
    {
        // would usually get a connection
        return null;
    }

    interface BusinessLogic<V>
    {
        V execute(Connection connection) throws Exception;
    }
}

это будет скомпилировано, и он правильно установил бы, что правильный тип значения HashSet<>(10) должен быть String.