Как написать лямбда с типизированным параметром без необработанных типов - PullRequest
0 голосов
/ 01 октября 2018

У меня есть класс Foo с параметром универсального типа

static class Foo<T> {

    T get() {return null;}

    void set(T t) {}

}

Я хочу определить экземпляр java.util.function.Consumer, который работает для ЛЮБОГО Foo независимо от его аргумента универсального типа.Потребитель просто вызовет метод set для экземпляра Foo и передаст значение, возвращаемое методом get.Я решил использовать Lambda для реализации потребителя:

Consumer<Foo> compilesButWithWarnings = foo -> foo.set(foo.get());

К сожалению, я получаю предупреждения с этой реализацией.Предупреждение:

The method set(Object) belongs to the raw type Foo. 
References to generic type Foo<T> should be parameterized.

Если я попытаюсь записать свою лямбду как:

Consumer<Foo<?>> compileError = foo -> foo.set(foo.get());

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

The method set(capture#1-of ?) in the type Foo<capture#1-of ?> is not 
applicable for the arguments (capture#2-of ?)

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

Consumer<Foo<?>> worksButRequiresStaticMethod = Test::setFoo;

static <ANY> void setFoo(Foo<ANY> foo) {
    foo.set(foo.get());
}

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

Большое спасибо.

Ответы [ 3 ]

0 голосов
/ 02 октября 2018

Если вы можете редактировать Foo, вы можете добавить метод для внутренней обработки:

void update() {
    set(get());
}

Затем просто используйте этот метод в качестве потребителя:

Consumer<Foo<?>> consumer = Foo::update;

В противном случае,Я думаю, что статический метод - единственный способ.Вам нужно избегать использования подстановочного типа <?> в точке, где вызывается set, поскольку единственное, что вы можете передать неограниченному параметру подстановочного метода, это null.

0 голосов
/ 02 октября 2018

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

Это будет работать:

Consumer<Foo<Object>> c = (Foo<Object> o) -> o.set(o.get());

Это экземпляр для этого конкретного универсального параметра.value (Object).

Вам нужен еще один «экземпляр» для каждого отдельного типа.

Имеет смысл?

Это все равно, что запросить переменную любого типа.

Переменная имеет только один тип.Известно во время компиляции.

0 голосов
/ 01 октября 2018

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

Например:

static class Foo<T> {
    T get() {return null;}
    void set(T t) {}
}
public static <T>Consumer<Foo<T>> getFooConsumer() {
    return foo -> foo.set(foo.get());
}
public static void main(String[] args) {
    Consumer<Foo<String>> cons = getFooConsumer();
    Foo<String> foo = new Foo<>();
    foo.set("foo!");
    cons.accept(foo);
}

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

Вы всегда можете продвинуть его дальше и добавить значение по вашему выбору в ваш потребляемый Foo, например:

public static <T>Consumer<Foo<T>> getFooConsumer(T t) {
    return foo -> foo.set(t);
}
...