Function接口compose方法中? super T的作用及灵活性解析
? super T的工作机制与灵活性提升 先明确Function接口中compose方法的核心作用:它用来把两个函数串联起来,先执行传入的before函数,再执行当前函数。方法签名如下:
default <V> Function<V, R> compose(Function<? super V, ? extends T> before) { Objects.requireNonNull(before); return (V v) -> apply(before.apply(v)); }
这里的? super V是针对before函数的输入类型做的通配符限制,我们拆解来看它的逻辑:
一、? super V的工作机制
? super V表示before函数的输入类型可以是V本身,或者V的任意父类。这背后依赖Java的多态特性:父类类型的引用可以接收子类类型的对象。
当调用组合后的函数时,会传入一个V类型的参数给before函数。如果before的输入类型是V的父类(比如Object是Integer的父类),这个V类型的参数依然能被正常接收——因为子类对象可以向上转型为父类类型。
举个具体例子:
- 现有
Function<Object, Integer> objToInt = obj -> Integer.parseInt(obj.toString());(输入Object,转成Integer) - 再有
Function<Integer, String> intToString = num -> "数字:" + num;
调用intToString.compose(objToInt)会得到一个Function<Object, String>。这里V是Object,? super V匹配Object,objToInt完全符合要求。再换个场景:
- 现有
Function<Number, Integer> numToInt = num -> num.intValue();(输入Number,转成Integer) - 想组合成
Function<Integer, String>,即intToString.compose(numToInt)
此时V是Integer,? super V包括Integer、Number、Object,numToInt的输入是Number(Integer的父类),依然符合限制,所以可以正常组合。如果没有? super V,而是限定before的输入必须是Integer,那numToInt就无法被传入。
二、为什么要用super提升灵活性
在集合场景中,? super T是为了允许向集合中添加T或其子类对象(比如List<? super Integer>可以add Integer、Byte等);而在Function.compose中,? super V的作用是放宽对before函数输入类型的限制,让更多函数可以被组合。
如果不用? super V,只能接受输入类型恰好是V的函数,这会极大限制组合的可能性。比如上面例子中,能处理Integer父类(Number、Object)的函数都无法被用来和intToString组合。而用了? super V之后,只要某个函数能接收V类型的输入(不管它的输入是V还是V的父类),都可以作为before传入,让函数组合的场景更丰富。
简单来说,? super V让compose方法不再挑剔before函数的输入范围,只要能处理我们要传入的V类型参数,就可以被串联,这就是它提升灵活性的核心原因。
内容的提问来源于stack exchange,提问作者GUST4

