Ric*_*gan 98 java lambda java-8
我看到java.util.function.BiFunction,所以我可以这样做:
BiFunction<Integer, Integer, Integer> f = (x, y) -> { return 0; };
Run Code Online (Sandbox Code Playgroud)
如果那还不够好我需要TriFunction怎么办?它不存在!
TriFunction<Integer, Integer, Integer, Integer> f = (x, y, z) -> { return 0; };
Run Code Online (Sandbox Code Playgroud)
我想我应该补充一点,我知道我可以定义自己的TriFunction,我只是想了解不在标准库中包含它的原理.
Ale*_*kka 144
如果您需要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));
}
}
Run Code Online (Sandbox Code Playgroud)
以下小程序显示了如何使用它.请记住,结果类型被指定为最后一个泛型类型参数.
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]
}
}
Run Code Online (Sandbox Code Playgroud)
我想如果TriFunction有实际用途,java.util.*或者java.lang.*它已被定义.我永远不会超过22个参数,但是;-)我的意思是,所有允许流集合的新代码都不需要TriFunction作为任何方法参数.所以它没有被包括在内.
UPDATE
为了完整性并遵循另一个答案(与currying相关)中的破坏性函数说明,下面是如何在没有附加接口的情况下模拟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
Run Code Online (Sandbox Code Playgroud)
当然,可以通过其他方式组合功能,例如:
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
Run Code Online (Sandbox Code Playgroud)
虽然curry对于支持lambda之外的函数式编程的任何语言都是很自然的,但Java不是以这种方式构建的,并且虽然可以实现,但代码很难维护,有时甚至可以阅读.但是,它作为练习非常有用,有时部分功能在您的代码中具有合适的位置.
小智 77
据我所知,只有两种功能,破坏性和建设性.
虽然建设性的功能,顾名思义,构建一些东西,一个破坏性的东西会摧毁一些东西,但不会像你现在想象的那样.
例如,功能
Function<Integer,Integer> f = (x,y) -> x + y
Run Code Online (Sandbox Code Playgroud)
是一个建设性的.因为你需要构建一些东西.在示例中,您构造了元组(x,y).构造函数存在问题,即无法处理无限参数.但最糟糕的是,你不能只是打开一个论点.你不能只说"好吧,让x:= 1"并尝试你想尝试的每一个.每次整个元组都需要构造
x := 1.所以,如果你想看看函数返回的内容,y := 1, y := 2, y := 3你必须写f(1,1) , f(1,2) , f(1,3).
在Java 8中,应该使用方法引用来处理(大多数情况下)构造函数,因为使用构造型lambda函数没有太大的优势.它们有点像静态方法.你可以使用它们,但它们没有真正的状态.
另一种类型是破坏性的,它需要一些东西并根据需要将其拆除.例如,破坏性功能
Function<Integer, Function<Integer, Integer>> g = x -> (y -> x + y)
Run Code Online (Sandbox Code Playgroud)
与具有f建设性的功能相同.破坏性函数的好处是,你现在可以处理无限的参数,这对于流特别方便,你可以让参数保持打开状态.所以,如果你想再次看看会的结果是什么样的,如果x := 1和y := 1 , y := 2 , y := 3,可以说h = g(1)和
h(1)是结果为y := 1,h(2)为y := 2和h(3)为y := 3.
所以在这里你有一个固定的状态!这是非常有活力的,而且大多数时候我们想要的是lambda.
像Factory这样的模式更容易,如果你可以放入一个为你工作的功能.
破坏性的很容易相互结合.如果类型合适,您可以根据自己的喜好进行组合.使用它,您可以轻松定义使(使用不可变值)测试更容易的态射!
你也可以用一个有建设性的组合做到这一点,但破坏性的组合看起来更好,更像是一个列表或装饰,而建设性的组合看起来很像一棵树.像建设性功能回溯这样的事情并不好.您可以只保存破坏性的部分功能(动态编程),而在"回溯"中只使用旧的破坏性功能.这使得代码更小,更易读.使用建设性功能,您或多或少地记住所有参数,这可能很多.
那么为什么需要BiFunction更多的问题而不是为什么没有TriFunction呢?
首先,很多时候你只有几个值(少于3个)并且只需要一个结果,所以根本不需要正常的破坏性功能,建设性的就可以了.像monad这样的东西确实需要建设性的功能.但除此之外,根本没有很多充分的理由说明为什么会这样BiFunction.这并不意味着它应该被删除!我为Monads而战直到我死!
因此,如果您有许多参数,您无法将它们组合成逻辑容器类,并且如果您需要该函数是建设性的,请使用方法引用.否则尝试使用新获得的破坏性功能,你可能会发现自己用很少的代码行做很多事情.
我有几乎相同的问题和部分答案.不确定建设性/解构性的答案是否是语言设计者的想法.我认为有3个以上的N有有效用例.
我来自.NET.在.NET中,你有虚函数的Func和Action.谓词和其他一些特殊情况也存在.请参阅:https://msdn.microsoft.com/en-us/library/bb534960(v = vs.110).aspx
我想知道为什么语言设计者选择Function,Bifunction并且在DecaExiFunction之前没有继续的原因是什么?
第二部分的答案是类型擦除.编译后,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);
}
}
Run Code Online (Sandbox Code Playgroud)
内部功能被用来规避另一个小问题.Eclipse坚持在同一目录中将两个类都放在名为Function的文件中......不确定这是否是一个编译器问题.但是我无法改变Eclipse中的错误.
Func用于防止与java Function类型的名称冲突.
因此,如果你想从3到16的参数中添加Func,你可以做两件事.
第二种方式的示例:
package eu.hanskruse.trackhacks.joepie.functions.tri;
@FunctionalInterface
public interface Func<T1,T2,T3,R>{
public R apply(T1 t1,T2 t2,T3 t3);
}
Run Code Online (Sandbox Code Playgroud)
和
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);
}
Run Code Online (Sandbox Code Playgroud)
什么是最好的方法?
在上面的例子中,我没有包含andThen()和compose()方法的实现.如果你添加这些,你必须添加16个重载:TriFunc应该有一个带有16个参数的andthen().由于循环依赖性,这会给你一个编译错误.你也不会有函数和BiFunction的这些重载.因此,您还应该使用一个参数定义Func,使用两个参数定义Func.在.NET中,循环依赖性将通过使用Java中不存在的扩展方法来规避.
小智 7
或者,添加以下依赖项,
<dependency>
<groupId>io.vavr</groupId>
<artifactId>vavr</artifactId>
<version>0.9.0</version>
</dependency>
Run Code Online (Sandbox Code Playgroud)
现在,您可以使用Vavr函数,例如以下最多8个参数,
3个参数:
Function3<Integer, Integer, Integer, Integer> f =
(a, b, c) -> a + b + c;
Run Code Online (Sandbox Code Playgroud)
5个参数:
Function5<Integer, Integer, Integer, Integer, Integer, Integer> f =
(a, b, c, d, e) -> a + b + c + d + e;
Run Code Online (Sandbox Code Playgroud)