Java 8:java.util.function中的TriFunction(和kin)在哪里? 或者有什么select?
我看到java.util.function.BiFunction,所以我可以这样做:
BiFunction<Integer, Integer, Integer> f = (x, y) -> { return 0; };
如果这不够好,我需要TriFunction? 它不存在!
TriFunction<Integer, Integer, Integer, Integer> f = (x, y, z) -> { return 0; };
我想我应该补充一点,我知道我可以定义我自己的TriFunction,我只是想了解没有包含在标准库中的基本原理。
据我所知,只有两种function,破坏性的和build设性的。
虽然build设性的function,顾名思义,构build的东西,破坏性的破坏的东西,但不是你现在可能会想的方式。
例如,function
Function<Integer,Integer> f = (x,y) -> x + y
是一个build设性的 。 因为你需要构build一些东西。 在这个例子中,你构造了元组(x,y) 。 build设性的function有问题,无法处理无限的论点。 但最糟糕的是,你不能只是打开一个论点。 你不能只说“好,让x:= 1”然后尝试每一个你可能想尝试的东西。 你必须每次构造x := 1
的整个元组。 所以,如果你想看看函数返回y := 1, y := 2, y := 3
你必须写f(1,1) , f(1,2) , f(1,3)
。
在Java 8中,构造函数应该通过使用方法引用来处理(大部分时间),因为使用构造性的lambda函数没有多大优势。 它们有点像静态方法。 你可以使用它们,但是它们没有真实的状态。
另一种types是破坏性的,需要的东西就会消失。 比如破坏性的function
Function<Integer, Function<Integer, Integer>> g = x -> (y -> x + y)
与build设性的函数f
一样。 破坏性函数的好处是,你现在可以处理无限的参数,这对stream是非常方便的,你可以把参数打开。 所以如果你再想看看结果是什么样子,如果x := 1
和y := 1 , y := 2 , y := 3
,你可以说h = g(1)
和h(1)
是y := 1
, h(2)
对于y := 2
, h(3)
对于y := 3
。
所以在这里你有一个固定的状态! 这是非常dynamic的,大多数情况下,我们希望从一个lambda。
像Factory这样的模式要简单得多,如果你只需要一个为你工作的函数就可以了。
破坏性的很容易相互结合。 如果types是正确的,你可以把它们组合起来,只要你喜欢。 使用这个,你可以很容易地定义态射使得(用不变的值)testing更容易!
你也可以用build设性的方法做到这一点,但破坏性的组合看起来更好,更像一个列表或装饰者,而build设性的一个看起来很像一棵树。 而像build设性function回溯的事情并不好。 你可以保存破坏性的(dynamic编程)的部分function,而在“回溯”只使用旧的破坏性function。 这使代码更小,更好的可读性。 具有build设性的function,你或多或less地记住所有的论据,这可能是很多的。
那么为什么需要BiFunction
应该更多的问题比为什么没有TriFunction
?
首先,很多时候你只有几个值(小于3),只需要一个结果,所以一个正常的破坏性函数根本就不需要,一个build设性的函数将会很好。 还有像monads这样的东西确实需要build设性的function。 但除此之外,根本没有什么充分的理由来说明BiFunction
。 这并不意味着它应该被删除! 我争取我的Monads,直到我死!
所以如果你有很多参数,你不能把它们合并到一个逻辑容器类中,而且如果你需要这个函数是有build设性的,那就使用一个方法引用。 否则,尝试使用新获得的破坏性function的能力,你可能会发现自己用很less的代码行做了很多事情。
如果您需要TriFunction,只需执行以下操作:
@FunctionalInterface interface TriFunction<A,B,C,R> { R apply(A a, B b, C c); default <V> TriFunction<A, B, C, V> andThen( Function<? super R, ? extends V> after) { Objects.requireNonNull(after); return (A a, B b, C c) -> after.apply(apply(a, b, c)); } }
以下小程序显示如何使用它。 请记住,结果types被指定为最后一个genericstypes参数。
public class Main { public static void main(String[] args) { BiFunction<Integer, Long, String> bi = (x,y) -> ""+x+","+y; TriFunction<Boolean, Integer, Long, String> tri = (x,y,z) -> ""+x+","+y+","+z; System.out.println(bi.apply(1, 2L)); //1,2 System.out.println(tri.apply(false, 1, 2L)); //false,1,2 tri = tri.andThen(s -> "["+s+"]"); System.out.println(tri.apply(true,2,3L)); //[true,2,3] } }
我猜如果在java.util.*
或java.lang.*
有TriFunction的实际使用,它将被定义。 我永远不会超越22个参数,但是;-)我的意思是,所有允许stream集合的新代码从不需要TriFunction作为任何方法参数。 所以不包括在内。
UPDATE
为了完整性和在另一个答案(与咖喱相关)中的破坏性function解释之后,下面是TriFunction如何在没有附加接口的情况下被仿真:
Function<Integer, Function<Integer, UnaryOperator<Integer>>> tri1 = a -> b -> c -> a + b + c; System.out.println(tri1.apply(1).apply(2).apply(3)); //prints 6
当然,也可以用其他方式来组合function,例如:
BiFunction<Integer, Integer, UnaryOperator<Integer>> tri2 = (a, b) -> c -> a + b + c; System.out.println(tri2.apply(1, 2).apply(3)); //prints 6 //partial function can be, of course, extracted this way UnaryOperator partial = tri2.apply(1,2); //this is partial, eq to c -> 1 + 2 + c; System.out.println(partial.apply(4)); //prints 7 System.out.println(partial.apply(5)); //prints 8
对于支持函数式编程超越lambdaexpression式的任何语言来说,currying是很自然的,Java不是以这种方式构build的,尽pipe可以实现,但代码很难维护,有时甚至是读取。 然而,这是一个非常有用的练习,有时部分函数在你的代码中有一个合理的位置。
我有几乎相同的问题和部分答案。 不确定build设性的/解构性的答案是语言devise者的想法。 我认为有3个以上的N有有效的用例。
我来自.NET。 而在.NET中,你有用于void函数的Func和Action。 谓词等一些特殊情况也存在。 请参阅: https : //msdn.microsoft.com/en-us/library/bb534960(v=vs.110).aspx
我不知道为什么语言devise者selectfunction,双function,并没有继续下去,直到DecaExiFunction的原因是什么?
第二部分的答案是types删除。 编译之后,Func和Func没有区别。 因此下面不编译:
package eu.hanskruse.trackhacks.joepie; public class Functions{ @FunctionalInterface public interface Func<T1,T2,T3,R>{ public R apply(T1 t1,T2 t2,T3 t3); } @FunctionalInterface public interface Func<T1,T2,T3,T4,R>{ public R apply(T1 t1,T2 t2,T3 t3, T4 t4); } }
内部函数被用来规避另一个小问题。 Eclipse坚持要在同一个目录中有名为Function的文件中的两个类…不知道现在是否这是一个编译器问题。 但是我不能把Eclipse中的错误。
Func被用来防止与java函数types的名字冲突。
所以如果你想从3到16的参数添加Func,你可以做两件事。
- 使TriFunc,TesseraFunc,PendeFunc,… DecaExiFunc等
- (我应该用希腊语还是拉丁语?)
- 使用包名称或类来使名称不同。
第二种方法的例子:
package eu.hanskruse.trackhacks.joepie.functions.tri; @FunctionalInterface public interface Func<T1,T2,T3,R>{ public R apply(T1 t1,T2 t2,T3 t3); }
和
package eu.trackhacks.joepie.functions.tessera; @FunctionalInterface public interface Func<T1,T2,T3,T4,R>{ public R apply(T1 t1,T2 t2,T3 t3, T4 t4); }
什么是最好的方法?
在上面的例子中,我没有包含和()和compose()方法的实现。 如果你添加了这些,你必须每个添加16个重载:TriFunc应该有一个带16个参数的andthen()。 这会给你一个编译错误,因为循环依赖。 你也不会有这些函数和BiFunction的重载。 因此,您还应该使用一个参数定义Func,使用两个参数定义Func。 在.NET中,循环依赖将通过使用Java中不存在的扩展方法来规避。