Java 8:java.util.function 中的 TriFunction(和 kin)在哪里?或者有什么替代方案?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/18400210/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Java 8: Where is TriFunction (and kin) in java.util.function? Or what is the alternative?
提问by Richard Finegan
I see java.util.function.BiFunction, so I can do this:
我看到 java.util.function.BiFunction,所以我可以这样做:
BiFunction<Integer, Integer, Integer> f = (x, y) -> { return 0; };
What if that is not good enough and I need TriFunction? It doesn't exist!
如果这还不够好而我需要 TriFunction 怎么办?它不存在!
TriFunction<Integer, Integer, Integer, Integer> f = (x, y, z) -> { return 0; };
I guess I should add that I know I can define my own TriFunction, I'm just trying to understand the rationale behind not including it in the standard library.
我想我应该补充一点,我知道我可以定义自己的 TriFunction,我只是想了解不将其包含在标准库中的原因。
采纳答案by user3003859
As far as I know, there are only two kinds of functions, destructive and constructive.
据我所知,只有两种功能,破坏性和建设性。
While constructive function, as the name implies, constructs something, a destructive one destroys something, but not in the way you may think now.
顾名思义,建设性功能会构建某些东西,而破坏性功能会破坏某些东西,但与您现在认为的方式不同。
For example, the function
例如,函数
Function<Integer,Integer> f = (x,y) -> x + y
is a constructiveone.
As you need to construct something. In the example
you constructed the tuple (x,y). Constructive functions have the problem,
of being not able to handle infinite arguments. But the worst thing is, you
can't just leave an argument open. You can't just say "well, let x := 1" and try out
every y you may like to try. You have to construct every time the whole tuple with
x := 1
. So if you like to see what the functions return for y := 1, y := 2, y := 3
you
have to write f(1,1) , f(1,2) , f(1,3)
.
是一个建设性的。因为你需要构建一些东西。在示例中,您构造了元组(x,y)。构造函数存在无法处理无限参数的问题。但最糟糕的是,你不能让争论保持开放。你不能只是说“好吧,让 x := 1”然后尝试每个你可能想尝试的 y。每次都必须用
x := 1
. 因此,如果您想查看函数返回的内容,y := 1, y := 2, y := 3
则必须编写f(1,1) , f(1,2) , f(1,3)
.
In Java 8, constructive functions should be handled (most of the time) by using method references because there's not much advantage of using a constructive lambda function. They are a bit like static methods. You can use them, but they have no real state.
在 Java 8 中,构造函数应该(大部分时间)通过使用方法引用来处理,因为使用构造 lambda 函数没有太大的优势。它们有点像静态方法。你可以使用它们,但它们没有真实的状态。
The other type is the destructive one, it takes something and dismantles it as far as needed. For example, the destructivefunction
另一种是破坏性的,它需要一些东西并根据需要拆除它。例如破坏函数
Function<Integer, Function<Integer, Integer>> g = x -> (y -> x + y)
does the same as the function f
which was constructive. The benefits of a destructive function are, you
can handle now infinite arguments, which is especially convenient for streams, and you can just leave arguments open.
So if you again want to see what would the result be like if x := 1
and y := 1 , y := 2 , y := 3
, you can say h = g(1)
and
h(1)
is the result for y := 1
, h(2)
for y := 2
and h(3)
for y := 3
.
与具有f
建设性的功能相同。破坏性函数的好处是,您现在可以处理无限参数,这对于流来说尤其方便,并且您可以将参数保持打开状态。因此,如果您再次想查看 if x := 1
and的结果如何y := 1 , y := 2 , y := 3
,您可以说h = g(1)
and
h(1)
is the result for y := 1
, h(2)
fory := 2
和h(3)
for y := 3
。
So here you have a fixed state! That's quite dynamic and that's most of the time that what we want from a lambda.
所以在这里你有一个固定的状态!这是非常动态的,而且大多数时候这是我们想要从 lambda 中获得的。
Patterns like Factory are a lot easier if you can just put in a function which does the work for you.
如果您可以放入一个为您完成工作的函数,那么像 Factory 这样的模式会容易得多。
Destructive ones are easily combined with each other. If the type is right you can just compose them as you like. Using that, you can easily define morphisms which make (with immutable values) testing a lot easier!
破坏性的很容易相互结合。如果类型正确,您可以随心所欲地组合它们。使用它,您可以轻松定义态射,使(具有不可变值)测试变得更加容易!
You can do that too with a constructive one, but destructive composition looks nicer and more like a list or a decorator, and the constructive one looks a lot like a tree. And things like backtracking with constructive functions are just not nice. You can just save the partial functions of a destructive one (dynamic programming), and on "backtrack" just use the old destructive function. That makes code a lot smaller and better readable. With constructive functions you have more or less to remember all arguments, which can be a lot.
你也可以用建设性的组合来做到这一点,但破坏性的组合看起来更好,更像是一个列表或装饰器,而建设性的看起来很像一棵树。像使用建设性函数回溯这样的事情并不好。您可以只保存破坏性函数(动态编程)的部分函数,而在“回溯”时只需使用旧的破坏性函数。这使得代码更小,可读性更好。使用构造函数,您或多或少可以记住所有参数,这可能很多。
So why is there a need for BiFunction
should be more of question than why there is no TriFunction
?
那么为什么需要BiFunction
比为什么没有应该更多的问题TriFunction
呢?
First of all, a lot of time you just have a few values (less than 3) and need just a result, so a normal destructive function would not be needed at all, a constructive one would do fine. And there are things like monads which
really needs a constructive function. But aside from that, there are not really a lot of good reasons why there is a BiFunction
at all. Which doesn't mean it should be removed! I fight for my Monads until I die!
首先,很多时候你只有几个值(小于 3)并且只需要一个结果,所以根本不需要正常的破坏性函数,一个建设性的函数就可以了。还有像 monad 这样的东西真的需要一个建设性的功能。但除此之外,并没有很多很好的理由来说明为什么有一个BiFunction
。这并不意味着它应该被删除!我为我的单子而战,直到我死!
So if you have a lot of arguments, which you can't combine into a logical container class, and if you need the function to be constructive, use a method reference. Otherwise try to use the new gained ability of destructive functions, you may find yourself doing a lot of things with a lot less code lines.
因此,如果您有很多参数,您无法将它们组合成一个逻辑容器类,并且如果您需要该函数具有建设性,请使用方法引用。否则尝试使用新获得的破坏性函数的能力,您可能会发现自己用更少的代码行做了很多事情。
回答by Alex Pakka
If you need TriFunction, just do this:
如果您需要 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));
}
}
Following small program shows how it can be used. Remember that result type is specified as a last generic type parameter.
下面的小程序展示了它的使用方法。请记住,结果类型被指定为最后一个泛型类型参数。
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]
}
}
I guess if there was practical use for TriFunction in java.util.*
or java.lang.*
it would have been defined. I would never go beyond 22 arguments, though ;-) What I mean by that, all new code that allows to stream collections never required TriFunction as any of the method parameters. So it was not included.
我想如果 TriFunction 有实际用途,java.util.*
或者java.lang.*
它会被定义。不过,我永远不会超过 22 个参数;-) 我的意思是,所有允许流集合的新代码从不需要 TriFunction 作为任何方法参数。所以它没有被包括在内。
UPDATE
更新
For completeness and following the destructive functions explanation in another answer (related to currying), here is how TriFunction can be emulated without additional interface:
为了完整性并遵循另一个答案中的破坏性函数解释(与柯里化相关),这里是如何在没有额外接口的情况下模拟 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
Of course, it is possible to combine functions in other ways, e.g.:
当然,也可以通过其他方式组合功能,例如:
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
While currying would be natural to any language that supports functional programming beyond lambdas, Java is not built this way and, while achievable, the code is hard to maintain, and sometimes read. However, it is very helpful as an exercise, and sometimes partial functions have a rightful place in your code.
虽然对于支持 lambda 之外的函数式编程的任何语言来说,柯里化都是很自然的,但 Java 不是这样构建的,虽然可以实现,但代码很难维护,有时也很难阅读。但是,它作为练习非常有用,有时部分函数在您的代码中占有一席之地。
回答by Hans
I Have almost the same question and a partial answer. Not sure whether the constructive/deconstructive answer is what the language designers had in mind. I think having 3 and more upto N has valid use cases.
我有几乎相同的问题和部分答案。不确定建设性/解构性的答案是否是语言设计者的想法。我认为有 3 个或更多 N 个具有有效的用例。
I come from .NET. and in .NET you have Func and Action for void functions. Predicate and some other special cases also exist. See: https://msdn.microsoft.com/en-us/library/bb534960(v=vs.110).aspx
我来自.NET。在 .NET 中,您有用于 void 函数的 Func 和 Action。谓词和其他一些特殊情况也存在。请参阅:https: //msdn.microsoft.com/en-us/library/bb534960(v=vs.110).aspx
I wonder what the reason was why the language designers opted for Function, Bifunction and did not continue until DecaExiFunction?
我想知道为什么语言设计者选择了 Function, Bifunction 而没有继续到 DecaExiFunction 的原因是什么?
The answer to the second part is type erasure. After compilation there is no difference between Func and Func. The following therefore does not compile:
第二部分的答案是类型擦除。编译后,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);
}
}
Inner functions were used to circumvent another minor problem. Eclipse insisted on having both classes in files named Function in the same directory... Not sure whether this a compiler issue nowadays. But I cannot turn the error of in Eclipse.
内部函数被用来规避另一个小问题。Eclipse 坚持将两个类都放在同一目录中名为 Function 的文件中......现在不确定这是否是编译器问题。但是我无法在 Eclipse 中打开错误。
Func was used to prevent name clashes with the java Function type.
Func 用于防止与 java Function 类型的名称冲突。
So if you want to add Func from 3 upto 16 argument you can do two things.
因此,如果您想从 3 到 16 个参数中添加 Func,您可以做两件事。
- Make TriFunc, TesseraFunc,PendeFunc, ...DecaExiFunc etc
- (Should I use Greek or Latin?)
- Use package names or classes to make the names different.
- 制作 TriFunc、TesseraFunc、PendeFunc、...DecaExiFunc 等
- (我应该使用希腊语还是拉丁语?)
- 使用包名或类使名称不同。
Example for the second way:
第二种方式的例子:
package eu.hanskruse.trackhacks.joepie.functions.tri;
@FunctionalInterface
public interface Func<T1,T2,T3,R>{
public R apply(T1 t1,T2 t2,T3 t3);
}
and
和
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);
}
What would be the best approach?
最好的方法是什么?
In the above examples I did not include implementations for the andThen() and compose() methods. If you add these you must add 16 overloads each: the TriFunc should have an andthen() with 16 arguments. That would give you a compile error because of circular dependencies. Also you would not have these overloads for Function and BiFunction. Therefore you should also define Func with one argument and Func with two arguments. In .NET circular dependencies would be circumvented by using extension methods which are not present in Java.
在上面的例子中,我没有包括 andThen() 和 compose() 方法的实现。如果添加这些,则必须分别添加 16 个重载:TriFunc 应该有一个带 16 个参数的 andthen()。由于循环依赖,这会给你一个编译错误。您也不会对 Function 和 BiFunction 进行这些重载。因此,您还应该定义带有一个参数的 Func 和带有两个参数的 Func。在 .NET 中,循环依赖将通过使用 Java 中不存在的扩展方法来规避。
回答by Vance
I found the source code for BiFunction here:
我在这里找到了 BiFunction 的源代码:
I modified it to create TriFunction. Like BiFunction, it uses andThen() and not compose(), so for some applications that require compose(), it may not be appropriate. It should be fine for normal kinds of objects. A good article on andThen() and compose() can be found here:
我修改它以创建 TriFunction。和 BiFunction 一样,它使用 andThen() 而不是 compose(),所以对于一些需要 compose() 的应用,它可能不太合适。对于普通类型的对象应该没问题。一篇关于 andThen() 和 compose() 的好文章可以在这里找到:
http://www.deadcoderising.com/2015-09-07-java-8-functional-composition-using-compose-and-andthen/
http://www.deadcoderising.com/2015-09-07-java-8-functional-composition-using-compose-and-andthen/
import java.util.Objects;
import java.util.function.Function;
/**
* Represents a function that accepts two arguments and produces a result.
* This is the three-arity specialization of {@link Function}.
*
* <p>This is a <a href="package-summary.html">functional interface</a>
* whose functional method is {@link #apply(Object, Object)}.
*
* @param <S> the type of the first argument to the function
* @param <T> the type of the second argument to the function
* @param <U> the type of the third argument to the function
* @param <R> the type of the result of the function
*
* @see Function
* @since 1.8
*/
@FunctionalInterface
public interface TriFunction<S, T, U, R> {
/**
* Applies this function to the given arguments.
*
* @param s the first function argument
* @param t the second function argument
* @param u the third function argument
* @return the function result
*/
R apply(S s, T t, U u);
/**
* Returns a composed function that first applies this function to
* its input, and then applies the {@code after} function to the result.
* If evaluation of either function throws an exception, it is relayed to
* the caller of the composed function.
*
* @param <V> the type of output of the {@code after} function, and of the
* composed function
* @param after the function to apply after this function is applied
* @return a composed function that first applies this function and then
* applies the {@code after} function
* @throws NullPointerException if after is null
*/
default <V> TriFunction<S, T, U, V> andThen(Function<? super R, ? extends V> after) {
Objects.requireNonNull(after);
return (S s, T t, U u) -> after.apply(apply(s, t, u));
}
}
回答by Amol Damodar
Alternative is, add the below dependency,
替代方法是,添加以下依赖项,
<dependency>
<groupId>io.vavr</groupId>
<artifactId>vavr</artifactId>
<version>0.9.0</version>
</dependency>
Now, you can use Vavr Function, like below upto 8 arguments,
现在,您可以使用 Vavr 函数,如下所示最多 8 个参数,
3 arguments:
3个参数:
Function3<Integer, Integer, Integer, Integer> f =
(a, b, c) -> a + b + c;
5 arguments:
5 个参数:
Function5<Integer, Integer, Integer, Integer, Integer, Integer> f =
(a, b, c, d, e) -> a + b + c + d + e;
回答by Leandro Maro
You could also create your own function taking the 3 parameters
您还可以使用 3 个参数创建自己的函数
@FunctionalInterface
public interface MiddleInterface<F,T,V>{
boolean isBetween(F from, T to, V middleValue);
}
MiddleInterface<Integer, Integer, Integer> middleInterface =
(x,y,z) -> x>=y && y<=z; // true
回答by Koushik Roy
You can't always stop at TriFunction. Sometimes, you may need to pass n number of parameters to your functions. Then support team will have to create an QuadFunction to fix your code. Long term solution would be to create an Object with the extra parameters and then use the ready-made Function or BiFunction.
您不能总是停留在 TriFunction。有时,您可能需要将 n 个参数传递给您的函数。然后支持团队必须创建一个 QuadFunction 来修复您的代码。长期解决方案是创建一个带有额外参数的对象,然后使用现成的函数或 BiFunction。