将泛型类型参数标记为Java 8中的功能接口

时间:2014-06-24 08:57:47

标签: java generics lambda java-8 java-stream

我想将函数的type参数限制为功能接口。

这样的事情:

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

此处不允许@FunctionalInterface

目的是使得可以返回具有类型参数类型的lambda。由于T可以是普通类,因此不允许返回lambda。

是否有可能将类型参数限制为功能接口?

2 个答案:

答案 0 :(得分:6)

正如your other question已经讨论的那样,不可能这样做。不仅要注释类型参数,还要特别是通过lambda表达式实现未知接口。

编译方法时

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

编译器必须生成能够返回相应类型实例的代码,但它不知道T将是什么,因为这取决于foo()的调用者,但编译之间没有关联foo()并编译foo()的来电者。后者可能在几年之后发生在我们这个星球的另一边。

也许你不知道Type Erasure。您的方法foo()只有一个编译版本,但它必须履行返回相应类型实例的通用协定。 不知道 T

这在返回现有实例时有效,例如返回集合的元素或作为参数传递的值之一。但是泛型方法无法返回类型参数的新实例。不使用new,也不使用lambda表达式。


请注意,如果让知道类型的调用者执行“up-level”操作,仍然可以使用所需函数的子接口实现。假设您有一个Generic工厂方法,如:

public static <A,B> Function<A,B> getter(Map<?, ? extends B> map) {
    return a->map.get(a);
}

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

现在,如果您的来电者具有BFunction的任意子类型,例如

X

它可以使用您的工厂方法生成interface X extends Function<String, Integer> {} 的实例,其中修饰该函数,如:

X

此处,在呼叫者网站上检查Map<String, Integer> map=new HashMap<>(); X x=getter(map)::apply; x.apply("foo"); 作为功能接口的约束。

答案 1 :(得分:2)

我的仿制药很弱,但我认为那会是:

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

但我认为你不能实例化 R,你必须能够从T获得它。您的代码不知道R的运行时类型,因此new R()超出范围。

但是,例如,如果T可以为您提供R,就像Map一样:

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

返回一个getter,当使用给定的map调用时,将返回带有用于创建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);
    }
}

输出:

Lower: a
Upper: A

我不认为类型擦除会让你比这更接近,除非使用实例方法并参数化你的包含类。