Java 8 Lambda表达式的函数式编程– Monads

什么是monad ?: monad是一种设计模式概念,用于大多数功能编程语言(如Lisp)或现代世界的Clojure或Scala中。 (实际上,我会从scala复制一些内容。)现在,为什么它在Java中变得很重要? 因为java从版本8获得了新的lambda功能。Lambda或闭包是一种功能编程功能。 它使您可以将代码块用作变量,并像这样传递代码块。 我在上一篇文章Java 8中的烹饪-项目Lambda中讨论了Java的“项目Lambda”。 现在,您可以在此处提供的JDK 8预览版中进行尝试。 现在我们可以在Java 8之前做monad吗? 当然,毕竟Java的lambda在语义上只是实现接口的另一种方式(实际上并不是因为编译器知道它的使用位置),而是太多混乱的代码,几乎可以杀死其实用程序。

现在,让我在Java中建立一个用例,就像没有monad一样,而不是向您描述一个抽象的,看似毫无意义的想法。

讨厌的null检查:如果您编写了任何非平凡的(例如Hello-World)java程序,则可能已经做了一些null检查。 它们就像编程必不可少的弊端,您不能没有它们,但是它们会使您的程序杂乱无章。 让我们以带有一组Java数据对象的以下示例为例。 注意,无论如何我都没有使用过反模式的getter或setter。

public static class Userdetails{public Address address;public Name name;}public static class Name{public String firstName;public String lastName;        
}public static class Address{public String houseNumber;public Street street;public City city;}public static class Street{public String name;        
}public static class City{public String name;        
}

现在说您想从UserDetails用户访问街道名称,并且任何属性都可能为null。 没有monad,您可能会编写如下代码。

if(user == null )return null;
else if(user.address == null)return null;
else if(user.address.street == null)return null;
elsereturn user.address.street.name;

理想情况下,它应该是单线的。 我们真正关心的代码周围杂乱无章。 现在让我们看看如何解决这个问题。 让我们创建一个代表可选值的Option类。 然后让我们有一个map方法,该方法将在其包装后的值上运行lambda并返回另一个选项。 如果包装的值为null,则它将返回一个包含null的Option而不处理lambda,从而避免使用null指针异常。 请注意,map方法实际上需要使用lambda作为参数,但是我们将需要创建一个接口SingleArgExpression来支持该方法。

SingleArgExpression.java

package com.geekyarticles.lambda;public interface SingleArgExpression<P, R> {public R function(P param);
}

Option.java

package com.geekyarticles.javamonads;import com.geekyarticles.lambda.public class Option<T> {T value;public Option(T value){this.value = value;}public <E> Option<E> map(SingleArgExpression<T,E> mapper){if(value == null){return new Option<E>(null);}else{return new Option<E>(mapper.function(value));}}    @Overridepublic boolean equals(Object rhs){if(rhs instanceof Option){Option o = (Option)rhs;if(value == null) return (o.value==null);else{return value.equals(o.value);}}else{return false;}}@Overridepublic int hashCode(){return value==null? 0 : value.hashCode();}public T get(){return value;}}

OptionExample.java

package com.geekyarticles.javamonads.examples;import com.geekyarticles.javamonads.public class OptionExample{public static class Userdetails{public Option<Address> address = new Option<>(null);public Option<Name> name = new Option<>(null);}public static class Name{public Option<String> firstName = new Option<String>(null);public Option<String> lastName = new Option<String>(null);        }public static class Address{public Option<String> houseNumber;public Option<Street> street;public Option<City> city;}public static class Street{public Option<String> name;        }public static class City{public Option<String> name;        }public static void main(String [] args){Option<Userdetails> userOpt =  new Option<>(new Userdetails());//And look how simple it is nowString streetName = userOpt.flatMap(user -> user.address).map(address -> address.street).map(street -> street.name).get();System.out.println(streetName);}}

所以现在,基本上的想法是,只要方法有机会返回null,就返回Option。 将确保该方法的使用者理解该值可以为null,并且还使该使用者隐式地通过null检查,如图所示。 现在,我们从所有可能必须返回null的方法中返回Option,那么映射内的表达式也可能会将Option用作返回类型。 为了避免每次都调用get(),我们可以有一个与map类似的方法flatMap,除了它接受一个Option作为传递给它的lambda的返回类型。

public <E> Option<E> flatMap(SingleArgExpression<T, Option<E>> mapper){if(value == null){return new Option<E>(null);}return  mapper.function(value);}

我要说的最后一种方法是过滤器。 它将使我们在映射链中放置一个if条件,以便仅当条件为true时才获得一个值。 请注意,这也是null安全的。 在此特定的monad中,filter的用法并不明显,但稍后我们将看到其用法。 以下是一个示例,其中所有可为空的字段都已升级为Option,因此将flatMap用于地图的读取。

Option.java

package com.geekyarticles.javamonads;import com.geekyarticles.lambda.public class Option<T> {T value;public Option(T value){this.value = value;}public <E> Option<E> map(SingleArgExpression<T,E> mapper){if(value == null){return new Option<E>(null);}else{return new Option<E>(mapper.function(value));}}public <E> Option<E> flatMap(SingleArgExpression<T, Option<E>> mapper){if(value == null){return new Option<E>(null);}return  mapper.function(value);}public Option<T> filter(SingleArgExpression<T, Boolean> filter){if(value == null){return new Option<T>(null);}else if(filter.function(value)){return this;}else{return new Option<T>(null);}}@Overridepublic boolean equals(Object rhs){if(rhs instanceof Option){Option o = (Option)rhs;if(value == null) return (o.value==null);else{return value.equals(o.value);}}else{return false;}}@Overridepublic int hashCode(){return value==null? 0 : value.hashCode();}public T get(){return value;}}

OptionExample.java

package com.geekyarticles.javamonads.examples;import com.geekyarticles.javamonads.public class OptionExample{public static class Userdetails{public Option<Address> address = new Option<>(null);public Option<Name> name = new Option<>(null);}public static class Name{public Option<String> firstName = new Option<String>(null);public Option<String> lastName = new Option<String>(null);        }public static class Address{public Option<String> houseNumber;public Option<Street> street;public Option<City> city;}public static class Street{public Option<String> name;        }public static class City{public Option<String> name;        }public static void main(String [] args){//This part is just the setup code for the example to workOption<Userdetails> userOpt =  new Option<>(new Userdetails());userOpt.get().address = new Option<>(new Address());userOpt.get().address.get().street=new Option<>(new Street());userOpt.get().address.get().street.get().name = new Option<>("H. Street");//And look how simple it is nowString streetName = userOpt.flatMap(user -> user.address).flatMap(address -> address.street).flatMap(street -> street.name).get();System.out.println(streetName);}}

集合和Monad: Monad对集合框架也很有用。 尽管最好的方法是让每个收集类自己成为最佳性能的单子(将来可能会成为单子),但目前我们可以将它们包装起来。 这也带来了必须破坏类型检查系统的问题,因为我们事先不知道构建器的通用返回类型。

NoArgExpression.java

package com.geekyarticles.lambda;public interface NoArgExpression<R> {public R function();
}

SingleArgExpression.java

package com.geekyarticles.lambda;public interface SingleArgExpression<P, R> {public R function(P param);
}

CollectionMonad.java

package com.geekyarticles.javamonads;import com.geekyarticles.lambda.
import java.util.Collection;
import java.util.ArrayList;
import java.util.Arrays;public class CollectionMonad<T> {Collection<T> value;NoArgExpression<Collection> builder;public CollectionMonad(Collection<T> value, NoArgExpression<Collection> builder){this.value = value;this.builder = builder;}public CollectionMonad(T[] elements){this.value = new ArrayList<T>(elements.length);this.value.addAll(Arrays.asList(elements));this.builder = () -> new ArrayList();}@SuppressWarnings("unchecked")public <E> CollectionMonad<E> map(SingleArgExpression<T,E> mapper){Collection<E> result = (Collection<E>)builder.function();        for(T item:value){result.add(mapper.function(item));}    return new CollectionMonad<E>(result, builder);        }//What flatMap does is to flatten out the CollectionMonad returned by the lambda that is provided//It really shrinks a nested loop.@SuppressWarnings("unchecked")public <E> CollectionMonad<E> flatMap(SingleArgExpression<T, CollectionMonad<E>> mapper){Collection<E> result = (Collection<E>)builder.function();        for(T item:value){CollectionMonad<E> forItem = mapper.function(item);for(E e : forItem.get()){result.add(e);}}return new CollectionMonad<E>(result, builder);}@SuppressWarnings("unchecked")public CollectionMonad<T> filter(SingleArgExpression<T, Boolean> filter){Collection<T> result = (Collection<T>)builder.function();        for(T item:value){if(filter.function(item)){result.add(item);}}                return new CollectionMonad<T>(result, builder);            }public Collection<T> get(){return value;}@Overridepublic String toString(){        return value.toString();}}

ListMonadTest.java

package com.geekyarticles.javamonads.examples;import com.geekyarticles.javamonads.
import java.util.public class ListMonadTest {public static void main(String [] args){mapExample();flatMapExample();filterExample();}public static void mapExample(){List<Integer> list = new ArrayList<>();list.add(10);list.add(1);list.add(210);list.add(130);list.add(2);CollectionMonad<Integer> c = new CollectionMonad<>(list, () -> new ArrayList());//Use of mapSystem.out.println(c.map(v -> v.toString()).map(v -> v.charAt(0)));System.out.println();}public static void flatMapExample(){List<Integer> list = new ArrayList<>();list.add(10);list.add(1);list.add(210);list.add(130);list.add(2);CollectionMonad<Integer> c = new CollectionMonad<>(list, () -> new ArrayList());//Use of flatMapSystem.out.println(c.flatMap(v -> new CollectionMonad<Integer>(Collections.nCopies(v,v), () -> new ArrayList())));System.out.println();}public static void filterExample(){List<Integer> list = new ArrayList<>();list.add(10);list.add(1);list.add(210);list.add(130);list.add(2);CollectionMonad<Integer> c = new CollectionMonad<>(list, () -> new ArrayList());//Use of flatMap and filterSystem.out.println(c.flatMap(v -> new CollectionMonad<Integer>(Collections.nCopies(v,v), () -> new ArrayList())).filter(v -> v<=100));System.out.println();}}

乍一看,在这里使用flatmap似乎很麻烦,因为我们需要从lambda创建一个CollectionMonad。 但是,如果考虑使用嵌套的for循环的等效代码,它仍然非常简洁。

Streams和Monads:在这一点上,您可能正在考虑InputStream,但我们将讨论比这更笼统的内容。 流基本上是可能是无限的序列。 例如,可以使用公式或实际上是InputStream来创建它。 就像Iterator一样,我们将具有具有hasNext()和next()方法的流。 实际上,我们将使用Iterator接口,以便可以使用增强的for循环。 但是,我们还将使流变为单声道。 这种情况特别有趣,因为流可能是无限的,因此映射必须返回延迟处理lambda的流。 在我们的示例中,我们将创建具有特定分布的专用随机数生成器。 通常,所有值都是同等概率的。 但是我们可以通过映射来改变它。 让我们看一下示例以更好地理解。

让我们创建一个可以包装任意Iterator的通用Stream。 这样,我们也可以使用它现有的收集框架。

Stream.java

package com.geekyarticles.javamonads;import java.util.Iterator;
import com.geekyarticles.lambda.
import java.util.NoSuchElementException;public class Stream<T> implements Iterable<Option<T>>, Iterator<Option<T>>{//Provides a map on the underlying streamprivate class MapperStream<T,R> extends Stream<R>{private Stream<T> input;private SingleArgExpression<T, R> mapper;public MapperStream(Stream<T> input, SingleArgExpression<T, R> mapper){this.input = input;this.mapper = mapper;}@Overridepublic Option<R> next(){if(!hasNext()){//This is to conform to Iterator documentationthrow new NoSuchElementException();}return input.next().map(mapper);}@Overridepublic boolean hasNext(){return input.hasNext();}}//Provides a flatMap on the underlying streamprivate class FlatMapperStream<T,R> extends Stream<R>{private Stream<T> input;private SingleArgExpression<T, Stream<R>>  mapper;private Option<Stream<R>> currentStream = new Option<>(null);public FlatMapperStream(Stream<T> input, SingleArgExpression<T, Stream<R>> mapper){this.input = input;this.mapper = mapper;}@Overridepublic Option<R> next(){if(hasNext()){return currentStream.flatMap(stream -> stream.next());}else{//This is to conform to Iterator documentationthrow new NoSuchElementException();}}@Overridepublic boolean hasNext(){if(currentStream.map(s -> s.hasNext()) //Now Option(false) and Option(null) should be treated same.equals(new Option<Boolean>(Boolean.TRUE))){return true;}else if(input.hasNext()){currentStream=input.next().map(mapper);return hasNext();}else{return false;}}}//Puts a filter on the underlying streamprivate class FilterStream<T> extends Stream<T>{private Stream<T> input;private SingleArgExpression<T, Boolean> filter;private Option<T> next = new Option<>(null);public FilterStream(Stream<T> input, SingleArgExpression<T, Boolean> filter){this.input = input;this.filter = filter;updateNext();}public boolean hasNext(){return next != null;            }//We always keep one element calculated in advance.private void updateNext(){next = input.hasNext()? input.next(): new Option<T>(null);if(!next.map(filter).equals(new Option<Boolean>(Boolean.TRUE))){if(input.hasNext()){updateNext();                    }else{next = null;                    }}}public Option<T> next(){Option<T> res = next;updateNext();        if(res == null){throw new NoSuchElementException();}    return res;}}protected Iterator<T> input;public Stream(Iterator<T> input){this.input=input;}//Dummy constructor for the use of subclassesprotected Stream(){}@Overridepublic boolean hasNext(){return input.hasNext();}@Overridepublic Option<T> next(){return new Option<>(input.next());}@Overridepublic void remove(){throw new UnsupportedOperationException();}public <R> Stream<R> map(SingleArgExpression<T,R> mapper){return new MapperStream<T, R>(this, mapper);}public <R> Stream<R> flatMap(SingleArgExpression<T, Stream<R>> mapper){return new FlatMapperStream<T, R>(this, mapper);}public Stream<T> filter(SingleArgExpression<T, Boolean> filter){        return new FilterStream<T>(this, filter);}public Iterator<Option<T>> iterator(){return this;}}

StreamExample.java

package com.geekyarticles.javamonads.examples;import com.geekyarticles.javamonads.
import java.util.public class StreamExample{public static void main(String [] args){iteratorExample();infiniteExample();}static void iteratorExample(){System.out.println("iteratorExample");List<Integer> l = new ArrayList<>();l.addAll(Arrays.asList(new Integer[]{1,2,5,20,4,51,7,30,4,5,2,2,1,30,9,2,1,3}));Stream<Integer> stream = new Stream<>(l.iterator());//Stacking up operations//Multiply each element by 10 and only select if less than 70//Then take the remainder after dividing by 13for(Option<Integer> i : stream.map(i -> i*10).filter(i ->  i < 70).map(i -> i%13)){System.out.println(i.get());}System.out.println();}static void infiniteExample(){System.out.println("infiniteExample");Iterator<Double> randomGenerator = new Iterator<Double>(){@Overridepublic Double next(){return Math.random();}@Overridepublic boolean hasNext(){//Infinite iteratorreturn true;}public void remove(){throw new UnsupportedOperationException();}};Stream<Double> randomStream = new Stream<>(randomGenerator);//Now generate a 2 digit integer every second, for ever.for(Option<Integer> val:randomStream.map(v -> (int)(v*100))){System.out.println(val.get());try{Thread.sleep(1000);}catch(InterruptedException ex){ex.printStackTrace();}}}}

这个例子很复杂,所以花一些时间阅读一下。 但是,Stream类仅需要创建一次。 一旦到达,它就可以包装任何Iterator,它将为您免费提供所有monadic功能。

在我的下一篇文章中,我将解释更多单子。

参考: Java 8 Lambda表达式的函数式编程-来自GCG 伙伴 Jbas 合作伙伴 Debasish Ray Chawdhuri的Monad,来自Geeky Articles博客。

翻译自: https://www.javacodegeeks.com/2014/03/functional-programming-with-java-8-lambda-expressions-monads.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/364889.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

srtvlet filter

Filter&#xff0c;过滤器&#xff0c;顾名思义&#xff0c;即是对数据等的过滤&#xff0c;预处理过程。为什么要引入过滤器呢&#xff1f;在平常访问网站的时候&#xff0c;有时候发一些敏感的信息&#xff0c;发出后显示时 就会将敏感信息用*等字符替代&#xff0c;这就是用…

mysql怎么合并行_mysql怎么合并行

mysql合并行的方法&#xff1a;使用函数【GROUP_CONCAT()】&#xff0c;代码为【SELECT am.activeId,GROUP_CONCAT(m.modelName SEPARATOR ‘,’) modelName】。【相关学习推荐&#xff1a;mysql学习】mysql合并行的方法&#xff1a;一个字段可能对应多条数据&#xff0c;用mys…

ubuntu 14 编译ARM g2o-20160424

1. 安装eigen sudo apt-get install libeigen3-dev sudo apt-get install libsuitesparse-dev sudo apt-get install libqglviewer-dev 对于ARM版本&#xff0c;可以下载http://eigen.tuxfamily.org&#xff0c;之后放置在toolchain可以找到的三方库位置处&#xff0c;我使用的…

将旧项目从Ant迁移到Maven的4个简单步骤

一段时间以来&#xff0c;我们一直在考虑将构建从蚂蚁移植到Maven。 它发生在上个月&#xff0c;实际上比我们预期的要简单。 根据我的经验&#xff0c;这里简要介绍了我们遵循的步骤。 我们的应用程序是一个具有多个框架和技术的企业Web应用程序构建&#xff0c;并作为单个WAR…

浅谈servlet与jsp的关系

servlet是用java语言编写的&#xff0c;是一个java类。主要功能是用来接受、处理客户端的请求&#xff0c;并把处理结果返回到客户端显示。Jsp是servlet发展后期的产物。在没有jsp之前&#xff0c;servlet利用输出流动态生成整个HTML页面&#xff0c;输出内容包括每一个HTML标签…

折腾Java设计模式之建造者模式

博文原址&#xff1a;折腾Java设计模式之建造者模式 建造者模式 Separate the construction of a complex object from its representation, allowing the same construction process to create various representations. 将复杂对象的构造与其表现分离&#xff0c;允许相同的构…

python小甲鱼练习题答案_小甲鱼Python第 013讲元组:戴上了枷锁的列表 | 课后测试题及参考答案...

测试题&#xff1a;0. 请用一句话描述什么是列表&#xff1f;再用一句话描述什么是元组&#xff1f;列表&#xff1a;一个大仓库&#xff0c;可以随时往里面添加和删除任何东西。元祖&#xff1a;封闭的列表&#xff0c;一旦定义&#xff0c;就不可改变(不能添加、删除或修改)1…

获得的经验:ActiveMQ,Apache Camel和连接池

每隔一段时间&#xff0c;我会遇到一个与ActiveMQ的连接和池相关的有趣问题&#xff0c;而今天&#xff0c;我想讨论一些并不总是很清楚的问题&#xff0c;并且在使用ActiveMQ和Camel JMS时可能会导致您大量饮酒。 并不是说您无论如何都不会在使用ActiveMQ和Camel时大量喝酒………

羊车门问题

题目描述&#xff1a;有3扇关闭的门&#xff0c;一扇门后面停着汽车&#xff0c;其余门后是山羊&#xff0c;只有主持人知道每扇门后面是什么。参赛者可以选择一扇门&#xff0c;在开启它之前&#xff0c;主持人会开启另外一扇门&#xff0c;露出门后的山羊&#xff0c;然后允许…

webapp优化

1. 优化前提&#xff1a; 业务架构与数据库设计 2. 单页web应用 &#xff1a; ExtJs backbone ng avalon 框架&#xff1a; React Native &#xff0c; ionic &#xff0c; Mui, metror,WeeX,device one Meteor(版本: 1.0) 的另一个特点是它会通过手机内存中运行的 mi…

高版本Sqlserver数据库导入低版本Sqlserver

今天想跑一个关于java网站的demo&#xff0c;结果在附加数据库项这一块出现问题&#xff0c;例程的数据库用的是sqlserver2014&#xff0c;而我的是2008&#xff0c;添加数据库出现错误。经过一番查找&#xff0c;也找到某人写的一些博客上的解决方案&#xff0c;不过不是很清楚…

mysql删除bin-log_删除MYSQl BIN-LOG 日志

1.查找当前有哪些二进制日志文件&#xff1a;mysql> show binary logs;-----------------------------| Log_name | File_size |-----------------------------| mysql-bin.000001 | 1357315 || mysql-bin.000002 | 117 || mysql-bin.000003 | 404002 ||…

WildFly 8与GlassFish 4 –选择哪个应用服务器

自从我上一个博客以来已经有一段时间了。 我显然忙于其他事情&#xff0c;包括我的主要工作。 在对应用服务器的正确选择提出更多疑问之后&#xff0c;是时候再次讨论这个话题并分享我的想法。 该博客上最常阅读的文章之一是有关选择哪种Java EE 6应用程序服务器的文章 。 我一…

Vue--- 一点车项目

一点车项目 cli脚手架 组件化 数据交互路由指向存入数据库 前端页面 cli脚手架的安装与搭建 创建对应包 页面组件化编辑 &#xff08;共享组件&#xff1a;摘取出来一模一样的组件重用&#xff09;&#xff08;私有组件:在自己的组件写入 引入共享组件&#xff09; 数据交…

设计模式:模式或反模式,这就是问题

我最近遇到了Wiki页面“ Anti-pattern” &#xff0c;其中包含详尽的反模式列表。 其中一些对我来说很明显。 他们中的一些让我想了一下&#xff0c;其他的让我想了更多。 然后&#xff0c;我开始在页面上查找反模式“ singleton”&#xff0c;但找不到。 &#xff08;文本搜索…

Redis的散列类型

Redis是采用字典结构以key-value的形式存储数据的&#xff0c;在散列类型&#xff08;所谓的hash&#xff09;中的value也是一种字典结构。如果用关系表结构去理解&#xff0c;就是key为对象&#xff0c;value是属性和属性值。如下图&#xff1a; 所以使用散列&#xff08;hash…

python configparser 注释_使用configpar添加注释

如果您想去掉尾随的&#xff0c;可以按照atomopter的建议将ConfigParser.ConfigParser子类化&#xff0c;并实现自己的write方法来替换原来的方法&#xff1a;import sysimport ConfigParserclass ConfigParserWithComments(ConfigParser.ConfigParser):def add_comment(self, …

easy html css tree 简单的HTML css导航树

code: show: 更多专业前端知识&#xff0c;请上 【猿2048】www.mk2048.com

使用Maven和WebLogic 12c开发Java EE应用程序

WebLogic Server 12c现在对Maven提供了很好的支持。 不过&#xff0c;此文档有点被隐藏&#xff0c;因此这里是直接链接http://docs.oracle.com/middleware/1212/core/MAVEN 总而言之&#xff0c;Oracle没有为其服务器工件提供公共的Maven存储库管理器托管。 但是&#xff0c;…

Java实现并发线程中线程范围内共享数据

---恢复内容开始--- 利用Map&#xff0c;HashMap键值对的数据结构&#xff0c;实现并发线程中线程范围内数据共享。 package cn.qy.heima2;import java.util.HashMap; import java.util.Map; import java.util.Random;public class ThreadScopeShareData {private static int …