java 函数式编程
在许多文章中,我们探讨了针对不同语言的函数式编程概念,其中F#和Scala是对话的重点。 但是,由于我在工作场所进行过Java编程,因此探索这些相同的概念似乎很有趣并且令人大开眼界,因为自从我上次认真使用Java以来已有很长时间了。
高阶函数
如此处所述, 高阶函数是什么? 高阶函数是简单函数,可以将函数作为参数接收,并可以将另一个函数作为结果返回。
在现代Java中 ,我们可以轻松地做到这一点。 语法不是最好的,并且因为没有类型推断,我们必须显式声明函数类型,这在Java中意味着某种接口 。 让我们看看如何。
首先,假设我们有一个对象集合,也就是狗的集合,并且我们有一个作用于每只狗的函数。 我们希望能够在每个对象(狗)上调用此函数。
让我们看看如何创建这样的函数。
@FunctionalInterface
interface DogAge {
Integer apply ( Dog dog ) ;
}
List < Integer > getAges ( List < Dog > dogs, DogAge f ) {
List < Integer > ages &#61; new ArrayList <>() ;
for ( Dog dog : dogs ) {
ages.add ( f.apply ( dog )) ;
}
return ages;
}
我们定义一个给定狗的接口&#xff0c;它从中提取一些Integer值。 然后&#xff0c;我们定义一个函数getAges &#xff0c;该函数将传递的函数&#xff08;现在为接口 &#xff09;应用于每条狗。
现在&#xff0c;我们必须创建要应用于每只狗的实际功能。
DogAge f &#61; dog -> dog.getAge () ;
getAges( dogs, f ) ;
注意&#xff0c;我们不必像在旧版Java中那样实际定义DogAge实现。 这将是以下方式&#xff0c;但是请不要再使用它了。
DogAge dontUseMe &#61; new DogAge () {
&#64;Override
public Integer apply ( Dog dog ) {
return dog.getAge () ;
}
} ;
前者实际上是由编译器在看到第一个时生成的。
我们可以更进一步&#xff0c;并执行以下操作。
getAges( dogs, dog -> dog.getAge ()) ;
在这里&#xff0c;我们将函数直接传递给getAges方法。
不知何故&#xff0c; getAges是一个高阶函数&#xff0c;因为它可以接收函数作为参数。 Java通过接收接口来使签名保持怪异&#xff0c;但是我想这会在该语言的将来版本中得到改善。
为了有一个比较点&#xff0c;让我们在Scala中定义getAges并查看差异。 另外&#xff0c;我们将立即更改函数名称&#xff0c;以便更通用。
def extractStringFromDogs(dogs: List[Dog], f: Dog &#61;> String) &#61;
dogs.map(f)
在Java中 &#xff0c;我们可以做到。
&#64;FunctionalInterface
interface DogMapper {
String apply ( Dog dog ) ;
}
List extractStringFromDogs(List dogs, DogMapper f) {
return dogs.stream().map(dog -> f.apply(dog)).collect(Collectors.toList);
}
碰巧Java中已经有一个结构可以解决这个问题。 那就是Function 。 换句话说&#xff0c;我们可以做到。
List extractStringFromDogs(List dogs, Function f) {
return dogs.stream().map(dog -> f.apply(dog)).collect(Collectors.toList);
}
extractStringFromDogs(dogs, dog -> dog.getName());
现在&#xff0c;如何定义实际上返回其他函数的函数呢&#xff1f;
在Scala中 &#xff0c;我们可以执行以下操作。
scala> def sum(): (Int, Int) &#61;> Int &#61; (a, b) &#61;> a &#43; b
sum : ()(Int, Int) &#61;> Int
scala> sum()
res1 : (Int, Int) &#61;> Int &#61; $$Lambda$1067/2036949810&#64;715f45c6
scala> sum()(4,5)
res2 : Int &#61; 9
scala> res1(2, 3)
res3 : Int &#61; 5
在这里&#xff0c; sum返回一个可以在其他时间存储和评估的函数。 这是功能语言的非常强大且重要的构造。 我们可以在Java中做同样的事情吗&#xff1f;
让我们首先为这个特定问题定义我们自己的函数类型&#xff08; Functional Interface &#xff09;。
&#64;FunctionalInterface
interface TakeTwo {
Integer apply ( Integer a, Integer b ) ;
}
如我们所见&#xff0c; TakeTwo在语义上与我们在Scala中定义的相同。
现在&#xff0c;我们可以再次定义sum方法。
TakeTwo sum () {
return ( a, b ) -> a &#43; b;
}
TakeTwo mySum &#61; sum() ;
Integer finalSum &#61; mySum.apply ( 5, 6 ) ;
这与我们在Scala中所做的完全相同&#xff0c;只是在Scala中 &#xff0c;语法简洁明了&#xff0c;无需定义将函数接口用作函数类型。 是的&#xff0c;可以达到相同的结果。
再一次&#xff0c;我们实际上不必自己定义TakeTwo &#xff0c;因为在Java中已经定义了一个等效的接口BiFunction 。 通过使用它&#xff0c;我们可以通过以下方式写和 。
BiFunction < Integer, Integer, Integer > sum () {
return ( a, b ) -> a &#43; b;
}
更多功能接口。
为了支持函数式编程&#xff0c; Java集成了许多这些函数式接口 。 他们之中有一些是&#xff1a;
消费者
Java&#xff1a;
public interface Consumer < T > {
void accept ( T t ) ;
....
}
斯卡拉
谓词
Java
public interface Predicate < T > {
boolean test ( T t ) ;
...
}
斯卡拉
供应商
Java
public interface Supplier < T > {
T get () ;
}
斯卡拉
功能
Java
public interface Function < T, R > {
R apply ( T t ) ;
...
}
斯卡拉
双功能
Java
public interface BiFunction < T, U, R > {
R apply ( T t, U u ) ;
...
}
斯卡拉
这些只是新Java及其在Scala中的对等功能&#xff08; 功能接口 &#xff09;中的几种。 注意&#xff0c;在Scala中&#xff0c;我们不必为其定义任何接口&#xff0c;只需在其中具有函数即可&#xff0c;我们可以根据需要定义它们。
结论
Java肯定会以某种方式向函数式编程迈进&#xff0c;即使语法不是最方便的一种&#xff0c;其结果也是相同的。
另一方面&#xff0c; Scala语法要精确得多&#xff0c;并且可以更好地显示意图&#xff0c;而无需将接口创建为函数类型。
我只是希望Java继续发展&#xff0c;同时减少冗长程度并添加新的功能构造&#xff0c;因为最终&#xff0c;我们&#xff08;工程师&#xff09;才是从中获得真正好处的人。
翻译自: https://hackernoon.com/finally-functional-programming-in-java-ad4d388fb92e
java 函数式编程