Подтвердить что ты не робот

Вставить java.util.function.Function to Interface

Пример

В этом (упрощенном) примере я могу создать свой MyInterface -объект, используя ссылку метода на apply, но литье напрямую не работает.

@Test
public void testInterfaceCast(){
    Function<String, Integer> func = Integer::parseInt;

    MyInterface legal = func::apply; // works
    MyInterface illegal = func; // error
}

public interface MyInterface extends Function<String, Integer>{}

Второе присваивание дает ошибку компилятора:

incompatible types: Function<String,Integer> cannot be converted to MyInterface

Вопрос

Могу ли я сделать некоторую магию Generics, чтобы сделать Function<T, R> для интерфейса?

4b9b3361

Ответ 1

Причина MyInterface illegal = func; не работает, потому что func объявляется как переменная типа Function<String,Integer>, которая не является подтипом MyInterface.

Причина, по которой работает MyInterface legal = func::apply;, следующая. Вы могли бы ожидать, что тип fun::apply будет Function<String,Integer>, но это не так. Тип fun::apply зависит от того, какой тип компилятор ожидает. Поскольку вы используете его в контексте назначения, компилятор ожидает выражения типа MyInterface, и поэтому в этом контексте func::apply имеет тип MyInterface. Именно по этой причине это ссылочное выражение метода может появляться только в контекстах назначения, контекстах вызовов и контекстах каста (см. Спецификация языка Java).

Так как Function<String,Integer> не является подтипом MyInterface, литье func в MyInterface вызывает a ClassCastException. Поэтому самым ясным способом преобразования Function<String,Integer> в MyInterface является использование ссылки на метод, как вы это делали:

MyInterface legal = func::apply;

Ответ 2

В основе этого лежит тот факт, что, несмотря на весь симпатичный синтаксис, который придает внешнему виду внешний вид, Java не применяет структурные типы; правила Лысовской подстановочности названных типов сохраняются как никогда. В вашем реальном коде вы вызываете Function.andThen, который возвращает некоторый экземпляр Function и определенно не экземпляр MyInterface. Поэтому, когда вы пишете

MyInterface legal = func::apply;

вы получите еще один объект, который является экземпляром MyInterface. В отличие от этого, когда вы пишете

MyInterface illegal = func;

вы пытаетесь присвоить ссылку на существующий экземпляр Function непосредственно на illegal, что нарушит замещаемость Liskov, даже если оно будет соответствовать структурно. Никакой трюк не может существовать, чтобы обойти это в корне; даже если Java представила какой-то новый синтаксический сахар, который бы сделал его похожим на кастинг, фактические семантики по-прежнему будут иметь отношение к преобразованию (оценивая другой экземпляр).

Ответ 3

Он не компилируется по той же причине, что и этот фрагмент не компилируется:

Animal animal = new Animal();
Cat x = animal; //illegal assignment

то есть. вы подразумеваете, что каждое животное может быть кошкой. У компилятора нет доказательств того, что фактический экземпляр суперкласса действительно будет кошкой во время выполнения и поэтому вызывает ошибку.

Вы должны назначить Integer::parseInt экземпляру MyInterface:

MyInterface func = Integer::parseInt;
MyInterface illegal = func;

as func имеет тип Function<String, Integer> (т.е. супертип MyInterface).

Правда заключается в том, что во время компиляции Integer::parseInt типа MyInterface не существует Integer::parseInt если не назначить его для переменной MyInterface.