【问题标题】:Mark generic type parameter as functional interface in Java 8在 Java 8 中将泛型类型参数标记为功能接口
【发布时间】:2014-06-24 08:57:46
【问题描述】:

我想将函数的类型参数限制为函数式接口。

类似这样的:

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

这里不允许使用@FunctionalInterface

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

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

【问题讨论】:

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


    【解决方案1】:

    正如在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 表达式。


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

    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

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

    interface X extends Function<String, Integer> {}
    

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

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

    这里,X 是一个功能接口的约束在调用者站点上被检查。

    【讨论】:

    • @T.J.克劳德:你是对的,如果你不建造它,它就会起作用。在您的回答中它不起作用,因为Map 类型与Function 的返回类型不匹配,即Map 映射到Integer,而您的Function 将返回R extends Integer,这没有多大意义因为Integerfinal,但编译器仍然对此很挑剔,并坚持Integer 不能转换为R extends Integer
    • @T.J. Crowder:这就是问题所在:“匹配 Foo 和 Foo 的任何子类型”意味着,如果调用者愿意,调用者可以用 Foo 的子类型替换该类型。在这种情况下,您返回的 Function 必须返回 Foo 的子类型,但您无法实现这一点,因为 Map 的保证是返回 Foo。
    • LOL 我们走了不同的路。你做了一个 getter 绑定到你传入密钥的地图,我做了一个绑定到你传入地图的密钥的 getter。 :-)
    【解决方案2】:

    我的 generics-fu 很弱,但我认为应该是:

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

    输出:

    下:a
    上:A

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

    【讨论】:

    • 我希望返回类型是Function 的子类型。一些自己的类型,如A extends Function&lt;In, Out&gt;,而我希望能够在方法Foo 中放入Function 的所有可能子类型。
    猜你喜欢
    • 1970-01-01
    • 2020-09-16
    • 1970-01-01
    • 2018-02-10
    • 2012-04-28
    • 1970-01-01
    • 1970-01-01
    • 2016-04-16
    • 2017-09-21
    相关资源
    最近更新 更多