最后用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 = new ArrayList <>() ;

for ( Dog dog : dogs ) {
ages.add ( f.apply ( dog )) ;
}

return ages;
}

我们定义一个给定狗的接口,它从中提取一些Integer值。 然后,我们定义一个函数getAges ,将传递的函数(目前为接口 )应用于每只狗。

现在,我们必须创建要应用于每只狗的实际功能。

DogAge f = dog -> dog.getAge () ;

getAges( dogs, f ) ;

注意,我们不必像在旧版Java中那样实际定义DogAge实现。 这将是以下方式,但是请不要再使用它了。

DogAge dontUseMe = new DogAge () {
@Override
public Integer apply ( Dog dog ) {
return dog.getAge () ;
}
}
;

前者实际上是由编译器在看到第一个时生成的。

我们可以更进一步,并执行以下操作。

 getAges( dogs, dog -> dog.getAge ()) ;

在这里,我们将函数直接传递给getAges方法。

某种程度上, getAges是一个高阶函数,因为它可以接收函数作为参数。 Java通过接收接口来使签名保持怪异,但是我想这会在该语言的将来版本中得到改善。

为了有一个比较点,让我们在Scala中定义getAges并查看差异。 另外,我们将立即更改函数名称,以便更通用。

def extractStringFromDogs(dogs: List[Dog], f: Dog => String) = 
dogs.map(f)

Java中 ,我们可以做到。

@FunctionalInterface
interface DogMapper {
String apply ( Dog dog ) ;
}
List<String> extractStringFromDogs(List<Dog> dogs, DogMapper f) {         
    return dogs.stream().map(dog -> f.apply(dog)).collect(Collectors.toList);
}

碰巧Java中已经有一个结构可以解决这个问题。 那就是Function <A,B> 。 换句话说,我们可以做到。

List<String> extractStringFromDogs(List<Dog> dogs, Function<Dog, String> f) {
    return dogs.stream().map(dog -> f.apply(dog)).collect(Collectors.toList);
}
extractStringFromDogs(dogs, dog -> dog.getName());

现在,如何定义实际上返回其他函数的函数呢?

Scala中 ,我们可以执行以下操作。

scala> def sum(): (Int, Int) => Int = (a, b) => a + b
sum : ()(Int, Int) => Int
scala> sum()
res1 : (Int, Int) => Int = $$Lambda$1067/2036949810@715f45c6
scala> sum()(4,5)
res2 : Int = 9
scala> res1(2, 3)
res3 : Int = 5

在这里, sum返回一个可以在其他时间存储和评估的函数。 这是功能语言的非常强大且重要的构造。 我们可以在Java中做同样的事情吗?

让我们首先为这个特定问题定义我们自己的函数类型( Functional Interface )。

@FunctionalInterface
interface TakeTwo {
Integer apply ( Integer a, Integer b ) ;
}

如我们所见, TakeTwo在语义上与我们在Scala中定义的相同。

现在,我们可以再次定义sum方法。

TakeTwo sum () {
return ( a, b ) -> a + b;
}
TakeTwo mySum = sum() ;

Integer finalSum = mySum.apply ( 5, 6 ) ;

这与我们在Scala中所做的完全相同,只是在Scala中 ,语法简洁明了,无需定义将函数接口用作函数类型。 是的,可以达到相同的结果。

再一次,我们实际上不必自己定义TakeTwo ,因为在Java中已经定义了一个等效的接口BiFunction 。 通过使用它,我们可以通过以下方式写

BiFunction < Integer, Integer, Integer >  sum () {
return ( a, b ) -> a + b;
}
更多功能接口。

为了支持函数式编程, Java集成了许多这些函数式接口 。 他们之中有一些是:

消费者

Java:

public interface Consumer < T > {
void accept ( T t ) ;
....
}

斯卡拉

T => Unit
谓语

爪哇

public interface Predicate < T > {
boolean test ( T t ) ;
...
}

斯卡拉

T => boolean
供应商

爪哇

public interface Supplier < T > {
T get () ;
}

斯卡拉

:=> T
功能

爪哇

public interface Function < T, R > {
R apply ( T t ) ;
...
}

斯卡拉

T => R
双功能

爪哇

public interface BiFunction < T, U, R > {
R apply ( T t, U u ) ;
...
}

斯卡拉

(T, U) => R

这些只是新Java及其在Scala中的对等功能( 功能接口 )中的几种。 注意,在Scala中,我们不必为其定义任何接口,只需在其中具有函数即可,我们可以根据需要定义它们。

结论

Java肯定会以某种方式向函数式编程迈进,尽管语法不是最方便的一种,但结果是相同的。

另一方面, Scala语法要精确得多,并且可以更好地显示意图,而无需将接口创建为函数类型。

我只是希望Java继续发展,同时减少冗长程度并添加新的功能构造,因为最后,我们工程师将是从中获得真正好处的人。

From: https://hackernoon.com/finally-functional-programming-in-java-ad4d388fb92e

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值