如何编写具有通用类型参数的Lambda表达式而不使用原始类型

3

我有一个泛型类型参数的类Foo

static class Foo<T> {

    T get() {return null;}

    void set(T t) {}

}

我希望定义一个java.util.function.Consumer的实例,它适用于任何Foo而不考虑其泛型类型参数。该Consumer将简单地调用Foo实例的set方法,并传入由get方法返回的值。我决定使用Lambda来实现此Consumer:

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个回答

1
无法定义一个适用于任何泛型类型的实例。该特定实例必须知道其泛型类型,并且是编译器静态类型签名的一部分。
这将起作用:
Consumer<Foo<Object>> c = (Foo<Object> o) -> o.set(o.get());

这是针对特定泛型参数值(Object)的实例。

你需要为每个单独的类型再创建另一个“实例”。

明白了吗?

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

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


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

这是另一个选项,但我认为它并没有真正的改进。我在问题中提供的代码当然是非常基本的简化。我的实际用例要复杂得多。我想最干净的方法就是创建一个新的类文件,而不是将更多的代码放在lambda内部。 - DudeDoesThings
@DudeDoesThings 我有一种感觉,它可能更加复杂。如果你能够在更复杂的情况下总结使用案例,你可以随时更新你的问题(并让我知道,我可能会再看一眼)。顺便说一句,你不需要接受我的答案,只因为它是唯一的答案 - 有人(包括我自己)可能会在以后提供更好的答案 - 如果我是你,我会取消接受。 - Mena
当我提出这个问题时,我认为可能会有一些我之前从未听说过的功能现在在Java 9和Java 10中出现。当我学习Java时,它仍处于Java 7时期。但似乎没有“清洁”的解决方案。所以我想我也可以选择您的答案作为解决问题的方法。我只是接受它不能被解决到我一直希望的程度。 - DudeDoesThings

0

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

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

然后只需将该方法用作消费者:

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

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接