如何利用 Either 和 Option 进行函数式错误处理

来源:脚本之家  责任编辑:小易  

要交会员费?会员费是不是还没交?

前言

我将讨论 Scala 风格的模式匹配,但首先我需要通过 Either 概念建立一些背景知识。Either 的其中一个用法是函数式风格的错误处理,我会在本期文章中对其进行介绍。

回答和翻译如下: bena配置报下边的错误 Element type "bean" must be followed by either attribute specifications, ">" or "/>". 纳 元素类型“bean”必须遵循以下属性规范“>”或“/”。 < bean >

在 Java 中,错误的处理在传统上由异常以及创建和传播异常的语言支持进行。但是,如果不存在结构化异常处理又如何呢?许多函数式语言不支持异常范式,所以它们必须找到表达错误条件的替代方式。在本文中,我将演示 Java 中类型安全的错误处理机制,该机制绕过正常的异常传播机制(并通过 Functional Java 框架的一些示例协助说明)。

The media could not be loaded, either because the server or network failed or because the format is not supported. 由于服务器或网络失败或不支持该格式,所以无法加载媒体。 The media could not be loaded, either because the server

函数式错误处理

用“suspended ”不能说是帐号已被封了。是表示"帐号暂停使用”,原因是:或者是域名被过度使用,或者是域名提供者的资源已耗尽(或不够用了,分配不过来了)。等有了资源,你的帐号就可以恢复使用了。

如果您想在 Java 中不使用异常来处理错误,最根本的障碍是语言的限制,因为方法只能返回单个值。但是,当然,方法可以 返回单个 Object(或子类)引用,其中可包含多个值。那么,我可以使用一个 Map 来启用多个返回值。请看看清单 1 中的 divide() 方法:

B 试题分析:Either; or要么……要么……;两个都,既……又……;Neither nor两者都不,既不是……也不是。句意:我的爸爸妈妈都关心我,我非常爱他们。结合语境可知选B。点评:连词在句子中主要起联系上下文的作用,他们之间的区别就是表示逻辑关系的不

清单 1. 使用 Map 处理多个返回值

会阴侧切术是指在分娩过程中,用侧切剪在会阴部做一斜形切口,并需要缝合修补的一种外科手术。一般由于初产妇的会阴较紧,阴道口的延展性也相对较差。所以当胎儿较大或分娩较快时,就可能会使产妇的会阴部位发生严重撕裂,为了避免这种情况的发生,医生就会根据分娩时的具体情况选择进行侧切。侧切的好处对于产妇来说,进行侧切一方面是为了保护阴道弹性,一方面也是为了避免其他部位再发生裂伤,且侧切的伤口较自然撕裂的伤口边缘整齐,既易于医生的缝合又能在术后快速愈合。而对于宝宝来说,进行侧切可以缩短其在产道停留的时间,即缩短了宝宝在阴道被挤压的时间,大大的减少了宝宝发生缺氧的情况。这些情况必须要侧切?头盆不称?会阴较紧、阴

public static Map<String, Object> divide(int x, int y) { Map<String, Object> result = new HashMap<String, Object>(); if (y == 0) result.put("exception", new Exception("div by zero")); else result.put("answer", (double) x / y); return result; }

当然,这个传球就像当年G6雷阿伦的那记三分。唯一的区别一个是杀死对手,一个是杀死自己虽然理论上传球不无不可,但是同样的詹姆斯甩掉了成为罪人得几率,就像现在大家只是讨论该不该传而已,不过同时詹姆斯也放弃了成为英雄得机会,这才是他最大得损失总决赛开大前,超乔丹之声一片雀跃,但是现在也几乎无人再提。可见一斑

在 清单 1 中,我创建了一个 Map,以 String 为键,并以 Object 为值。在 divide() 方法中,我输出 exception 来表示失败,或者输出 answer 来表示成功。清单 2 中对两种模式都进行了测试:

漂洋过海来看你。。。为你我用了半年的积蓄,漂洋过海的来看你,为了这次相遇,我连见面时的呼吸都反复练习,说实话这首歌我唱的时候想哭。。。虽然不曾异地,也未曾相恋。像我这样的人。。。我听毛不易的这首歌喜欢的他,像我这样优秀的人,本该灿烂过一生,怎么二十多年到头来,还在人海里浮沉。像我这样懦弱的人,凡事都要留几分,怎么曾经也会为了谁,选择奋不顾身。真好的,就像写的我一样。。。

清单 2. 使用 Map 测试成功与失败

恭喜题主马上成为有车一族。首次购车就能将保值率作为决策考虑因素之一,可见楼主理念挺先进和成熟。雅阁是理智之选,推荐题主入手白色。理由以下几点:1、根据问题描述,目测题主年纪不大,黑色过于沉闷,即便是商务场合,一个年轻人开黑色车也显得太老成没有生气,当然如果有司机开车开门是另外一副派头;2、题主位于南方城市,阳光充足,气温较高,白色是反射最强的颜色,可相对有效抵挡高温天气和老化;3、白色相对比较耐脏。这是很多人理解上的一个误区,以为黑色就耐脏,其实黑色是最不耐脏的。黑色的车落点灰尘上去就灰蒙蒙的,没有铮亮的感觉了,白色的话视觉上这种感觉就好很多。减少洗车次数也是对车漆的保护;4、白色车安全,因为

@Test public void maps_success() { Map<String, Object> result = RomanNumeralParser.divide(4, 2); assertEquals(2.0, (Double) result.get("answer"), 0.1); } @Test public void maps_failure() { Map<String, Object> result = RomanNumeralParser.divide(4, 0); assertEquals("div by zero", ((Exception) result.get("exception")).getMessage()); }

在 清单 2 中,maps_success 测试验证在返回的 Map 中是否存在正确的条目。maps_failure 测试检查异常情况。

这种方法有一些明显的问题。首先,Map 中的结果无论如何都不是类型安全的,它禁用了编译器捕获特定错误的能力。键的枚举可以略微改善这种情况,但效果不大。其次,该方法调用器并不知道方法调用是否成功,这加重了调用程序的负担,它要检查可能结果的词典。第三,没有什么能阻止这两个键都有值,这使得结果模棱两可。

我需要的是一种让我能够以类型安全的方式返回两个(或多个)值的机制。

Either 类

返回两个不同值的需求经常出现在函数式语言中,用来模拟这种行为的一个常用数据结构是 Either 类。在 Java 中,我可以使用泛型创建一个简单的 Either 类,如清单 3 所示:

清单 3. 通过 Either 类返回两个(类型安全的)值

public class Either<A,B> { private A left = null; private B right = null; private Either(A a,B b) { left = a; right = b; } public static <A,B> Either<A,B> left(A a) { return new Either<A,B>(a,null); } public A left() { return left; } public boolean isLeft() { return left != null; } public boolean isRight() { return right != null; } public B right() { return right; } public static <A,B> Either<A,B> right(B b) { return new Either<A,B>(null,b); } public void fold(F<A> leftOption, F<B> rightOption) { if(right == null) leftOption.f(left); else rightOption.f(right); } }

在 清单 3中,Either 旨在保存一个 left 或 right 值(但从来都不会同时保存这两个值)。该数据结构被称为不相交并集。一些基于 C 的语言包含 union 数据类型,它可以保存含若干种不同类型的一个实例。不相交并集的槽可以保存两种类型,但只保存其中一种类型的一个实例。Either 类有一个 private 构造函数,使构造成为静态方法 left(A a) 或 right(B b) 的责任。在类中的其他方法是辅助程序,负责检索和调研类的成员。

利用 Either,我可以编写代码来返回异常或 一个合法结果(但从来都不会同时返回两种结果),同时保持类型安全。常见的函数式约定是 Either 类的 left 包含异常(如有),而 right 包含结果。

解析罗马数字

我有一个名为 RomanNumeral 的类(我将其实现​​留给读者去想象)和一个名为 RomanNumeralParser 的类,该类调用 RomanNumeral 类。parseNumber() 方法和说明性测试如清单 4 所示:

清单 4. 解析罗马数字

public static Either<Exception, Integer> parseNumber(String s) { if (! s.matches("[IVXLXCDM]+")) return Either.left(new Exception("Invalid Roman numeral")); else return Either.right(new RomanNumeral(s).toInt()); } @Test public void parsing_success() { Either<Exception, Integer> result = RomanNumeralParser.parseNumber("XLII"); assertEquals(Integer.valueOf(42), result.right()); } @Test public void parsing_failure() { Either<Exception, Integer> result = RomanNumeralParser.parseNumber("FOO"); assertEquals(INVALID_ROMAN_NUMERAL, result.left().getMessage()); }

在 清单 4 中,parseNumber() 方法执行一个验证(用于显示错误),将错误条件放置在 Either 的 left 中,或将结果放在它的 right中。单元测试中显示了这两种情况。

比起到处传递 Map,这是一个很大的改进。我保持类型安全(请注意,我可以按自己喜欢使异常尽量具体);在通过泛型的方法声明中,错误是明显的;返回的结果带有一个额外的间接级别,可以解压 Either 的结果(是异常还是答案)。额外的间接级别支持惰性。

惰性解析和 Functional Java

Either 类出现在许多函数式算法中,并且在函数式世界中如此之常见,以致 Functional Java 框架(参阅 参考资料)也包含了一个 Either 实现,该实现将在 清单 3 和 清单 4 的示例中使用。但它的目的就是与其他 Functional Java 构造配合使用。因此,我可以结合使用 Either 和 Functional Java 的 P1 类来创建惰性 错误评估。惰性表达式是一个按需执行的表达式(参阅 参考资料)。

在 Functional Java 中,P1 类是一个简单的包装器,包括名为 _1() 的方法,该方法不带任何参数。(其他变体:P2 和 P3 等,包含多种方法。)P1 在 Functional Java 中用于传递一个代码块,而不执行它,使您能够在自己选择的上下文中执行代码。

在 Java 中,只要您 throw 一个异常,异常就会被实例化。通过返回一个惰性评估的方法,我可以将异常创建推迟到以后。请看看清单 5 中的示例及相关测试:

清单 5. 使用 Functional Java 创建一个惰性解析器

public static P1<Either<Exception, Integer>> parseNumberLazy(final String s) { if (! s.matches("[IVXLXCDM]+")) return new P1<Either<Exception, Integer>>() { public Either<Exception, Integer> _1() { return Either.left(new Exception("Invalid Roman numeral")); } }; else return new P1<Either<Exception, Integer>>() { public Either<Exception, Integer> _1() { return Either.right(new RomanNumeral(s).toInt()); } }; } @Test public void parse_lazy() { P1<Either<Exception, Integer>> result = FjRomanNumeralParser.parseNumberLazy("XLII"); assertEquals((long) 42, (long) result._1().right().value()); } @Test public void parse_lazy_exception() { P1<Either<Exception, Integer>> result = FjRomanNumeralParser.parseNumberLazy("FOO"); assertTrue(result._1().isLeft()); assertEquals(INVALID_ROMAN_NUMERAL, result._1().left().value().getMessage()); }

清单 5 中的代码与 清单 4 中的类似,但多了一个 P1 包装器。在 parse_lazy 测试中,我必须通过在结果上调用 _1() 来解压结果,该方法返回 Either 的 right,从该返回值中,我可以检索值。在 parse_lazy_exception 测试中,我可以检查是否存在一个 left,并且我可以解压异常,以辨别它的消息。

在您调用 _1() 解压 Either 的 left 之前,异常(连同其生成成本昂贵的堆栈跟踪)不会被创建。因此,异常是惰性的,让您推迟异常的构造程序的执行。

提供默认值

惰性不是使用 Either 进行错误处理的惟一好处。另一个好处是,您可以提供默认值。请看清单 6 中的代码:

清单 6. 提供合理的默认返回值

public static Either<Exception, Integer> parseNumberDefaults(final String s) { if (! s.matches("[IVXLXCDM]+")) return Either.left(new Exception("Invalid Roman numeral")); else { int number = new RomanNumeral(s).toInt(); return Either.right(new RomanNumeral(number >= MAX ? MAX : number).toInt()); } } @Test public void parse_defaults_normal() { Either<Exception, Integer> result = FjRomanNumeralParser.parseNumberDefaults("XLII"); assertEquals((long) 42, (long) result.right().value()); } @Test public void parse_defaults_triggered() { Either<Exception, Integer> result = FjRomanNumeralParser.parseNumberDefaults("MM"); assertEquals((long) 1000, (long) result.right().value()); }

在 清单 6 中,假设我不接受任何大于 MAX 的罗马数字,任何企图大于该值的数字都将被默认设置为 MAX。parseNumberDefaults() 方法确保默认值被放置在 Either 的 right 中。

包装异常

我也可以使用 Either 来包装异常,将结构化异常处理转换成函数式,如清单 7 所示:

清单 7. 捕获其他人的异常

public static Either<Exception, Integer> divide(int x, int y) { try { return Either.right(x / y); } catch (Exception e) { return Either.left(e); } } @Test public void catching_other_people_exceptions() { Either<Exception, Integer> result = FjRomanNumeralParser.divide(4, 2); assertEquals((long) 2, (long) result.right().value()); Either<Exception, Integer> failure = FjRomanNumeralParser.divide(4, 0); assertEquals("/ by zero", failure.left().value().getMessage()); }

在 清单 7 中,我尝试除法,这可能引发一个 ArithmeticException。如果发生异常,我将它包装在 Either 的 left 中;否则我在 right 中返回结果。使用 Either 使您可以将传统的异常(包括检查的异常)转换成更偏向于函数式的风格。

当然,您也可以惰性包装从被调用的方法抛出的异常,如清单 8 所示:

清单 8. 惰性捕获异常

public static P1<Either<Exception, Integer>> divideLazily(final int x, final int y) { return new P1<Either<Exception, Integer>>() { public Either<Exception, Integer> _1() { try { return Either.right(x / y); } catch (Exception e) { return Either.left(e); } } }; } @Test public void lazily_catching_other_people_exceptions() { P1<Either<Exception, Integer>> result = FjRomanNumeralParser.divideLazily(4, 2); assertEquals((long) 2, (long) result._1().right().value()); P1<Either<Exception, Integer>> failure = FjRomanNumeralParser.divideLazily(4, 0); assertEquals("/ by zero", failure._1().left().value().getMessage()); }

嵌套异常

Java 异常有一个不错的特性,它能够将若干种不同的潜在异常类型声明为方法签名的一部分。尽管语法越来越复杂,但 Either 也可以做到这一点。例如,如果我需要 RomanNumeralParser 上的一个方法允许我对两个罗马数字执行除法,但我需要返回两种不同的可能异常情况,那么是解析错误还是除法错误?使用标准的 Java 泛型,我可以嵌套异常,如清单 9 所示:

清单 9. 嵌套异常

public static Either<NumberFormatException, Either<ArithmeticException, Double>> divideRoman(final String x, final String y) { Either<Exception, Integer> possibleX = parseNumber(x); Either<Exception, Integer> possibleY = parseNumber(y); if (possibleX.isLeft() || possibleY.isLeft()) return Either.left(new NumberFormatException("invalid parameter")); int intY = possibleY.right().value().intValue(); Either<ArithmeticException, Double> errorForY = Either.left(new ArithmeticException("div by 1")); if (intY == 1) return Either.right((fj.data.Either<ArithmeticException, Double>) errorForY); int intX = possibleX.right().value().intValue(); Either<ArithmeticException, Double> result = Either.right(new Double((double) intX) / intY); return Either.right(result); } @Test public void test_divide_romans_success() { fj.data.Either<NumberFormatException, Either<ArithmeticException, Double>> result = FjRomanNumeralParser.divideRoman("IV", "II"); assertEquals(2.0,result.right().value().right().value().doubleValue(), 0.1); } @Test public void test_divide_romans_number_format_error() { Either<NumberFormatException, Either<ArithmeticException, Double>> result = FjRomanNumeralParser.divideRoman("IVooo", "II"); assertEquals("invalid parameter", result.left().value().getMessage()); } @Test public void test_divide_romans_arthmetic_exception() { Either<NumberFormatException, Either<ArithmeticException, Double>> result = FjRomanNumeralParser.divideRoman("IV", "I"); assertEquals("div by 1", result.right().value().left().value().getMessage()); }

在 清单 9 中,divideRoman() 方法首先解压从 清单 4 的原始 parseNumber() 方法返回的 Either。如果在这两次数字转换的任一次中发生一个异常,Either left 与异常一同返回。接下来,我必须解压实际的整数值,然后执行其他验证标准。罗马数字没有零的概念,所以我制定了一个规则,不允许除数为 1:如果分母是 1,我打包我的异常,并放置在 right 的 left 中。

换句话说,我有三个槽,按类型划分:NumberFormatException、ArithmeticException 和 Double。第一个 Either 的 left 保存潜在的 NumberFormatException,它的 right 保存另一个 Either。第二个 Either 的 left 包含一个潜在的 ArithmeticException,它的 right 包含有效载荷,即结果。因此,为了得到实际的答案,我必须遍历 result.right().value().right().value().doubleValue()!显然,这种方法的实用性迅速瓦解,但它确实提供了一个类型安全的方式,将异常嵌套为类签名的一部分。

Option 类

Either 是一个方便的概念,在下期文章中,我将使用这个概念构建树形数据结构。Scala 中有一个名为 Option 的类与之类似,该类在 Functional Java 中被复制,提供了一个更简单的异常情况:none 表示不合法的值,some 表示成功返回。Option 如清单 10 所示:

清单 10. 使用 Option

public static Option<Double> divide(double x, double y) { if (y == 0) return Option.none(); return Option.some(x / y); } @Test public void option_test_success() { Option result = FjRomanNumeralParser.divide(4.0, 2); assertEquals(2.0, (Double) result.some(), 0.1); } @Test public void option_test_failure() { Option result = FjRomanNumeralParser.divide(4.0, 0); assertEquals(Option.none(), result); }

如 清单 10 所示,Option 包含 none() 或 some(),类似于 Either 中的 left 和 right,但特定于可能没有合法返回值的方法。

Functional Java 中的 Either 和 Option 都是单体,表示计算 的特殊数据结构,在函数式语言中大量使用。在下一期中,我将探讨有关 Either 的单体概念,并在不同的示例中演示它如何支持 Scala 风格的模式匹配。

结束语

当您学习一种新范式时,您需要重新考虑所有熟悉的问题解决方式。函数式编程使用不同的习惯用语来报告错误条件,其中大部分可以在 Java 中复制,不可否认,也有一些令人费解的语法。

扩展阅读,根据您访问的内容系统为您准备了以下内容,希望对您有帮助。

求详细介绍一下英语中的 主谓一致。我记得向 not only but also. either o

either..or / or / neither...nor / not only...but also/not...but 为就近原则,连接两个名词做主语时,谓语动词与离它较近的主语保持一致

both...and 谓语动词用复数

there be 句型,也为就近原则

当主语部分更with/ together with / alone with / as well as/ besides /expect 等其谓语动词当服输与主语在人称数上保持一致

全手打555 %>_<% 求采纳~~

You need to consider either a paid o

While seeking fulltime work,while 后面省略相同的主语时,后面用-ing 形式。本回答被网友采纳

用下列的话造句,每个造一句 1.be good for 2.taste 3.either....o

Exercise is good for your health.

It tastes sweet.

Either you or I can go to the concert.

Sometimes he goes for a walk after supper.

Don't be late for school.

There are lots of students in this school.

Let's take a walk after dinner, okay?更多追问追答追问是对的么?追答难道不对吗?追问。我只是问问错了我要投诉我明天作业对了,我给你好评,错了我就不给了追答对了,你应该讲奖励财富值翻倍,否则你就是小人一个。追问呵呵我知道我有啊你的嘴真脏我也翻倍了,答个题要翻倍,别人帮我弄作文时都没要哎追答最脏的是你,疑心重,还要投诉,别人热心答题是想得到你的投诉吗?念书念到哪里去了?自己失礼在先,有何面目求得免费答案?免费得到的大都是垃圾,你难道捡破烂吗?我可没有破烂货,让你捡便宜。追问好了噢噢,原来你捡破烂啊你先嘴贱的好吧你才小人一个,我起初说你什么了想想你要不要良心啊追答我只是问问

*-03-19 21:40

错了我要投诉

把“诉”换成“胎“,好不好?从19号到22号了,还没有完成使命?

the video could not be loaded,either because

用狐就可以吗

linux中使用unzip解压文件的时候总是提示错误

1:首先排除文件损坏的可能,将文件在Win下解压,看看是否能成功。

2:如果第1步在win下解压成功,那么就要想一下文件是怎样传到linux系统里的,如果是用ftp,那么上传的时候是否添加了bin参数,也就是从Win到linux的文件传输,一定要使用二进制形式来传输,如果用了ASCII模式,源文件会有所改变,所以在解压的时候就会出错。

  • 本文相关:
  • 通过实例学习either 树和模式匹配
  • java文件处理工具类详解
  • java实现的简单掷骰子游戏示例
  • java中的this指针使用方法分享
  • spring mvc过滤器-登录过滤的代码实现
  • mybatis整合spring 由于版本引起的bug问题
  • 详解elastic search搜索引擎在springboot中的实践
  • java web基础学习之开发环境篇(详解)
  • java初学者问题图解(动力节点java学院整理)
  • java容器arraylist知识点总结
  • 实例讲述java io文件复制
  • 求详细介绍一下英语中的 主谓一致。我记得向 not o...
  • You need to consider either a paid o
  • 用下列的话造句,每个造一句 1.be good for 2.tast...
  • the video could not be loaded,either because
  • linux中使用unzip解压文件的时候总是提示错误
  • The media could not be loaded, either because the
  • Element type "bean" must be followed by either ...
  • The media could not be loaded, either because t...
  • 网站This account has been suspended.Either the ...
  • __________ my father__________ my mother take g...
  • 免责声明 - 关于我们 - 联系我们 - 广告联系 - 友情链接 - 帮助中心 - 频道导航
    Copyright © 2017 www.zgxue.com All Rights Reserved