Разрешение перегрузки с помощью ссылок на методы и специализаций интерфейса функций для примитивных типов


Допустим, у нас есть класс и перегруженная функция:

public class Main {
    static final class A {
    }

    public static String g(ToIntFunction<? extends A> f) {
        return null;
    }

    public static String g(ToDoubleFunction<? extends A> f) {
        return null;
    }
}

И я хочу вызвать g со ссылкой на метод функции типа A - > int:

public class Main {
    static final class A {
    }

    public static String g(ToIntFunction<? extends A> f) {
        return null;
    }

    public static String g(ToDoubleFunction<? extends A> f) {
        return null;
    }

    private static int toInt(A x) {
        return 2;
    }

    public static void main(String[] args) {
        ToIntFunction<? extends A> f1 = Main::toInt;
        ToDoubleFunction<? extends A> f2 = Main::toInt;

        g(Main::toInt);
    }
}

Это работает с javac, но не с eclipse ecj. Я отправил сообщение об ошибке в ecj, но я не уверен, что это ошибка ecj или javac, и попытался следовать алгоритму разрешения перегрузки, чтобы выяснить это. Я считаю, что код должен быть принят, потому что интуитивно понятно, что ToIntFunction является лучшим соответствием для toInt чем ToDoubleFunction. Однако мое прочтение JLS заключается в том, что он должен быть отклонен, потому что нет никаких оснований для того, чтобы он был более конкретным.

Я должен признать, что я немного потерялся в спецификации JLS и был бы признателен за некоторую помощь. Сначала я хотел вычислить Тип Main::double2int, поэтому я посмотрел на 15.13.2. Тип ссылки на метод . Он не определяет тип, но определяет, когда тип совместим в разных контекстах:

Ссылка на метод выражение совместимо в контексте присваивания, контекст вызова или контекст приведения с целевым типом T, если T-это функциональный тип интерфейса (§9.8) и выражение соответствует тип функции типа наземной цели, производной от T.

Основными типами являются ToIntFunction<A> и ToDoubleFunction<A>. toInt возвращает int, который является присваиванием, совместимым с double, поэтому я бы заключил, что ссылка на метод совместима с ToIntFunction<? extends A> и ToDoubleFunction<? extends A> в контексте вызова. Это может быть проверено путем ассингинга ссылки метода как на ToIntFunction<? extends A>, так и на ToDoubleFunction<? extends A>, которая принимается в основной функции.

Затем я посмотрел на разрешение перегрузки и нашел 15.12.2.5. Выбор наиболее конкретного метода , который имеет частный случай для ссылок на метод, чтобы решить, какая из двух перегрузок для ToIntFunction или ToDoubleFunction более специфична для параметра Main::toInt объявления времени компиляции A -> int.

Функциональный интерфейс типа S более специфичен, чем функционал тип интерфейса T для выражения e, если T не является подтипом S и верно одно из следующих значений (где U1 ... Великобритания и R1 являются типы параметров и возвращаемого типа функции захвата С и В1 ... Vk и R2 - типы параметров и возвращаемый тип функции типа T):

...

Если e-точное выражение ссылки на метод (§15.13.1), то i) для все i (1 ≤ i ≤ k), Ui такое же, как Vi, и ii) одно из следующих верно:

R2-пустота.

R1

R1-примитивный тип, R2-ссылочный тип, а время компиляции объявление для ссылки на метод имеет возвращаемый тип, который является примитивный тип.

R1-ссылочный тип, R2-примитивный тип, а время компиляции объявление для ссылки на метод имеет возвращаемый тип, который является ссылочный тип.

Первое условие, очевидно, не совпадает, потому что R1 и R2 не совпадают. пустота.

Два интерфейса ToIntFunction и ToDoubleFunction отличаются только типами возвращаемых данных, которые являются примитивными типами double и int. Для примитивных типов предложение "R1 double и int, поэтому этот случай не определяет, какой тип более специфичен.

Последние две точки также не подходят, поскольку ни один из двух функциональных интерфейсов не имеет возвращаемого значения ссылочного типа.

Кажется, что нет правила для случая, когда два функциональных интерфейса возвращают примитивы и код должен быть отклонен как неоднозначный. Однако javac принимает код, и я ожидал бы, что он это сделает. Поэтому мне интересно, не является ли это упущенной точкой в JLS.

1 6

1 ответ:

Для примитивных типов предложение "R1 <:>

Это не так; double на самом деле является супертипом int.

Абзац 4.10:

Супертипы типа получаются рефлексивными и транзитивными замыкание над прямым отношением супертипа, записанное S >₁ T, который является определяется правилами, приведенными ниже в этом разделе. Мы пишем S :> T, чтобы укажите, что отношение супертипа выполняется между S и T.

Абзац 4.10.1:

Следующие правила определяют прямое отношение супертипа между примитивные типы:

double >₁ float

float >₁ long

long >₁ int

Отношениесупертипа , являющееся рефлексивным и транзитивным замыканием прямого отношение супертипа означает, что из (double >₁ float) ∧ (float >₁ long) ∧ (long >₁ int) следует double :> int.