在Java 8中将通用类型参数标记为函数式接口

4
我希望限制函数的类型参数为一个函数式接口。
类似这样:
```java public void myFunction(T fun) { // some code here } ```
public static <@FunctionalInterface T extends Function<A, B>> T foo () {
    return a -> bar;
}

@FunctionalInterface在这里不被允许。

目的是为了使返回类型为类型参数的lambda表达式成为可能。由于T可以是普通类,因此不允许返回lambda表达式。

有没有可能限制类型参数为函数式接口?

2个回答

6

正如在你的其他问题中已经讨论过的,这是不可能的。不仅无法注释类型参数,尤其是通过lambda表达式实现未知接口。

编译您的方法时

public static <@FunctionalInterface T extends Function<A, B>> T foo() {
    return a -> bar;
}

编译器必须生成能返回适当类型实例的代码,但它不知道 T 将是什么,因为这取决于调用 foo() 的人,但编译 foo() 和编译调用者之间没有联系。后者可能在地球另一边几年后才会发生。
也许您还不知道 类型擦除。您的方法 foo() 只有一个编译版本,但它必须满足通用合约,返回适当类型的实例。而不知道 T 是什么
当返回现有实例(例如集合中的元素或作为参数传递的值之一)时,此方法有效。但是,通用方法无法返回类型参数的新实例。既不能使用 new,也不能使用 lambda 表达式。
请注意,如果您让知道类型的调用者执行“上一级”操作,则仍然可以拥有所需函数的子接口实现。假设您有一个通用工厂方法,如下所示:
public static <A,B> Function<A,B> getter(Map<?, ? extends B> map) {
    return a->map.get(a);
}

这段代码适用于未知类型的 AB,以及未知的 Map 参数化类型,因为唯一的约束是方法 Map.get 接受 A 的实例,它接受 任何 类型并返回 B 的实例,因为任何类型 ? extends B 都可以分配给 B

现在,如果您的调用者具有 Function 的任意子类型 X,例如:

interface X extends Function<String, Integer> {}

它可以使用您的工厂方法来生成一个实例X,该实例装饰函数,例如:

Map<String, Integer> map=new HashMap<>();
X x=getter(map)::apply;
x.apply("foo");

在这里,对X作为函数式接口的限制在调用方进行检查。

1
@T.J. Crowder:您是正确的,如果您不构建它,它将会工作。在您的答案中,它不起作用,因为Map类型与Function的返回类型不匹配,即Map映射到Integer,而您的Function应该返回R extends Integer,这并没有太多意义,因为Integerfinal,但编译器仍然对此挑剔,并坚持Integer不能转换为R extends Integer - Holger
2
@T.J. Crowder: 这就是问题所在:“匹配Foo和任何Foo子类型”意味着,调用方可以根据需要将该类型替换为Foo的子类型。在这种情况下,您返回的Function必须返回该子类型的Foo,但由于Map的保证是返回Foo,因此您无法实现该要求。 - Holger
哈哈,我们走了不同的路。你写了一个绑定到传入键的地图的getter,而我写了一个绑定到传入地图的键的getter。 :-) - T.J. Crowder

2

我的泛型功力有限,但我认为正确的翻译应该是:

public static <T, R> Function<T, R> foo() {
    // ...
}

但我认为你无法实例化R,你需要从T中获取它。你的代码不知道R的运行时类型,因此new R()是超出范围的。

但例如,如果T可以像使用Map一样提供R

public static <K, R, T extends Map<K,R>> Function<T, R> makeGetter(K key) {
    return a -> a.get(key);
}

返回一个 getter,当使用给定的映射调用时,它将返回使用该 getter 创建时的键的条目:
import java.util.function.Function;
import java.util.*;
public class Example {

    public static final void main(String[] args) {
        Map<String,Character> mapLower = new HashMap<String,Character>();
        mapLower.put("alpha", 'a');
        mapLower.put("beta",  'b');

        Map<String,Character> mapUpper = new HashMap<String,Character>();
        mapUpper.put("alpha", 'A');
        mapUpper.put("beta",  'B');

        Function<Map<String, Character>, Character> getAlpha = makeGetter("alpha");

        System.out.println("Lower: " + getAlpha.apply(mapLower));
        System.out.println("Upper: " + getAlpha.apply(mapUpper));
    }

    public static <K, R, T extends Map<K,R>> Function<T, R> makeGetter(K key) {
        return a -> a.get(key);
    }
}

输出:

小写: a
大写: A

我认为除了使用实例方法和参数化包含类之外,类型擦除不能让你更接近这个目标。


我希望返回类型是Function的子类型。像A extends Function<In, Out>这样的自定义类型,而我想在方法Foo中能够放入Function的任何可能的子类型。 - F. Böller

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