如何使用不带原始类型的泛型类型参数编写Lambda

时间:2018-10-01 12:05:34

标签: java generics lambda compiler-errors compiler-warnings

我有一个带有通用类型参数的Foo类

static class Foo<T> {

    T get() {return null;}

    void set(T t) {}

}

我想定义一个适用于ANY Foo的java.util.function.Consumer实例,无论其泛型类型参数如何。使用者只需在Foo实例上调用set方法,并传入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.

如果我尝试将我的lambda写为:

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 :(得分:0)

另一个解决方法是使用为您提供使用者实例的通用方法。

例如:

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);
}

答案 1 :(得分:0)

如果您可以编辑Foo,则可以添加一种内部处理方法:

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

然后仅使用该方法作为使用者:

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

否则,我认为静态方法是唯一的方法。您需要避免在调用<?>时使用set通配符类型,因为唯一可以传递给无界通配符方法参数的是null

答案 2 :(得分:0)

不能定义可以与任何泛型类型一起使用的实例。对于该特定实例,泛型类型必须是已知的,并且是编译器静态类型签名的一部分。

这将起作用:

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

这是该特定通用参数值(对象)的实例。

每种单独的类型都需要另一个“实例”。

有道理吗?

这就像要求一个任何类型的变量。

变量只有一种类型。在编译时已知。