Optional的作用是什么?他都有哪些方法?阿里规范点名说尽量用Optional来避免空指针,那么什么场景用Optional?本篇文章围绕这三点来进行讲解。
目录
到目前为止,臭名昭著的空指针异常是导致Java应用程序失败的最常见原因。以前,为了解决空指针异常,Google公司著名的Guava项目引入了Optional类,Guava通过使用检查空值的方式来防止代码污染,它鼓励程序员写更干净的代码。受到Google Guava的启发,Optional类已经成为Java 8类库的一部分。
Optional 类(java.util.Optional) 是一个
容器类,它可以保存类型T的值,代表这个值存在。或者仅仅保存null,表示这个值不存在。原来用 null 表示一个值不存在,现在 Optional 可以更好的表达这个概念。并且可以避免空指针异常。
Optional提供很多有用的方法,这样我们就不用显式进行空值检测。
Optional类的Javadoc描述如下:这是一个可以为null的容器对象。
如果值存在则isPresent()方法会返回true,调用get()方法会返回该对象。
如果值不存在则isPresent()方法会返回false,调用get()方法会NPE。
创建Optional类对象的方法:
Optional.of(T t) : 创建一个 Optional 实例,t必须非空;Optional.empty() : 创建一个空的 Optional 实例Optional.ofNullable(T t):t可以为null判断Optional容器中是否包含对象:
boolean isPresent() : 判断是否包含对象void ifPresent(Consumer<? super T> consumer) :如果有值,就执行Consumer接口的实现代码,并且该值会作为参数传给它。获取Optional容器的对象:
T get(): 如果调用对象包含值,返回该值,否则抛异常T orElse(T other) :如果有值则将其返回,否则返回指定的other对象。T orElseGet(Supplier<? extends T> other) :如果有值则将其返回,否则返回由Supplier接口实现提供的对象。T orElseThrow(Supplier<? extends X> exceptionSupplier) :如果有值则将其返回,否则抛出由Supplier接口实现提供的异常。过滤:
Optional<T> filter(Predicate<? super <T> predicate):如果值存在,并且这个值匹配给定的 predicate,返回一个Optional用以描述这个值,否则返回一个空的Optional。映射
<U>Optional<U> map(Function<? super T,? extends U> mapper):如果有值,则对其执行调用映射函数得到返回值。如果返回值不为 null,则创建包含映射返回值的Optional作为map方法返回值,否则返回空Optional。<U> Optional<U> flatMap(Function<? super T, Optional<U>> mapper):如果值存在,就对该值执行提供的mapping函数调用,返回一个Optional类型的值,否则就返回一个空的Optional对象@Data
@AllArgsConstructor
@NoArgsConstructor
class Student {
private String name;
private Integer age;
}
public void test1() {
// 声明一个空Optional
Optional<Object> empty = Optional.empty();
// 依据一个非空值创建Optional
Student student = new Student();
Optional<Student> os1 = Optional.of(student);
// 可接受null的Optional
Student student1 = null;
Optional<Student> os2 = Optional.ofNullable(student1);
}
isPresent不带参数,判断是否为空,ifPresent可以选择带一个消费函数的实例。(isPresent和ifPresent一个是 is 一个是 if 注意一下哈)
public void test1() {
Student student = new Student();
Optional<Student> os1 = Optional.ofNullable(student);
boolean present = os1.isPresent();
System.out.println(present);
// 利用Optional的ifPresent方法做出如下:当student不为空的时候将name赋值为张三
Optional.ofNullable(student).ifPresent(p -> p.setName("张三"));
}
public void test1() throws Exception {
Student student = null;
Optional<Student> os1 = Optional.ofNullable(student);
// 使用get一定要注意,假如student对象为空,get是会报错的
// java.util.NoSuchElementException: No value present
Student student1 = os1.get();
// 当student为空的时候,返回我们新建的这个对象,有点像三目运算的感觉
Student student2 = os1.orElse(new Student("张三", 18));
// orElseGet就是当student为空的时候,返回通过Supplier供应商函数创建的对象
Student student3 = os1.orElseGet(() -> new Student("张三", 18));
// orElseThrow就是当student为空的时候,可以抛出我们指定的异常
os1.orElseThrow(() -> new Exception());
}
public void test1() {
Student student = new Student("李四", 3);
Optional<Student> os1 = Optional.ofNullable(student);
os1.filter(p -> p.getName().equals("张三")).ifPresent(x -> System.out.println("OK"));
}
map代码示例:
public void test1() {
Student student = new Student("李四", 3);
Optional<Student> os1 = Optional.ofNullable(student);
// 如果student对象不为空,就加一岁
Optional<Student> emp = os1.map(e ->
{
e.setAge(e.getAge() + 1);
return e;
});
}
这块的map说实话对lambda不是很熟练的 理解起来是很绕脑子的。
这里的map实际上就是用的Function函数,Function函数是有两个参数的,第一个是入参数据类型,第二个是返回数据类型。Function函数作用就是传入一个对象,然后返回一个对象,返回的对象类型可以自己设置。
T 就是代表实例的泛型数据类型,就是谁调用的 入参 必须跟调用者泛型的数据类型一样。
U 就是自己说了算,调用完map之后返回什么数据类型,那么U就设置什么

flatMap代码示例: flatMap跟map是一样的只不过他返回的是optional对象。
public static Optional<Integer> stringToInt(String s) {
try {
return Optional.of(Integer.parseInt(s));
} catch (NumberFormatException e) {
e.printStackTrace();
return Optional.empty();
}
}
Optional.ofNullable(props.getProperty(name))
.flatMap(OptionalUtils::stringToInt)
.filter(i -> i>0)
.orElse(0);
以前一直不懂Optional有啥用,感觉太无语了,Java8还把它当做一个噱头来宣传,最近终于发现它的用处了,当然不用函数式编程的话,是没感觉的;
如下提供了几个应用场景,基本上都是开发当中经常遇到的。
PatientInfo patientInfo = patientInfoDao.getPatientInfoById(consultOrder.getPatientId());
if (patientInfo != null) {
consultInfoResp.setPatientHead(patientInfo.getHead());
}
// 使用Optional 和函数式编程,一行搞定,而且像说话一样
Optional.ofNullable(patientInfo).ifPresent(p -> consultInfoResp.setPatientHead(p.getHead()));
public void test1() throws Exception {
Student student = new Student(null, 3);
if (student == null || isEmpty(student.getName())) {
throw new Exception();
}
String name = student.getName();
// 业务省略...
// 使用Optional改造
Optional.ofNullable(student).filter(s -> !isEmpty(s.getName())).orElseThrow(() -> new Exception());
}
public static boolean isEmpty(CharSequence str) {
return str == null || str.length() == 0;
}
public static String getChampionName(Competition comp) throws IllegalArgumentException {
if (comp != null) {
CompResult result = comp.getResult();
if (result != null) {
User champion = result.getChampion();
if (champion != null) {
return champion.getName();
}
}
}
throw new IllegalArgumentException("The value of param comp isn't available.");
}
这个在开发中是很常见的一种逻辑。去判读传进来的参数时候为空,或者是从数据库中获取的对象。由于某些原因,我们不能很流程的直接这样写。
comp.getResult().getChampion().getName()
上面的写法用Optional改写:
public static String getChampionName(Competition comp) throws IllegalArgumentException {
return Optional.ofNullable(comp)
.map(Competition::getResult) // 相当于c -> c.getResult(),下同
.map(CompResult::getChampion)
.map(User::getName)
.orElseThrow(()->new IllegalArgumentException("The value of param comp isn't available."));
}
类型之间的转换,并且当没有值的时候返回一个默认值
int timeout = Optional.ofNullable(redisProperties.getTimeout())
.map(x -> Long.valueOf(x.toMillis()).intValue())
.orElse(10000);
我正在学习如何使用Nokogiri,根据这段代码我遇到了一些问题:require'rubygems'require'mechanize'post_agent=WWW::Mechanize.newpost_page=post_agent.get('http://www.vbulletin.org/forum/showthread.php?t=230708')puts"\nabsolutepathwithtbodygivesnil"putspost_page.parser.xpath('/html/body/div/div/div/div/div/table/tbody/tr/td/div
总的来说,我对ruby还比较陌生,我正在为我正在创建的对象编写一些rspec测试用例。许多测试用例都非常基础,我只是想确保正确填充和返回值。我想知道是否有办法使用循环结构来执行此操作。不必为我要测试的每个方法都设置一个assertEquals。例如:describeitem,"TestingtheItem"doit"willhaveanullvaluetostart"doitem=Item.new#HereIcoulddotheitem.name.shouldbe_nil#thenIcoulddoitem.category.shouldbe_nilendend但我想要一些方法来使用
我有一个Ruby程序,它使用rubyzip压缩XML文件的目录树。gem。我的问题是文件开始变得很重,我想提高压缩级别,因为压缩时间不是问题。我在rubyzipdocumentation中找不到一种为创建的ZIP文件指定压缩级别的方法。有人知道如何更改此设置吗?是否有另一个允许指定压缩级别的Ruby库? 最佳答案 这是我通过查看rubyzip内部创建的代码。level=Zlib::BEST_COMPRESSIONZip::ZipOutputStream.open(zip_file)do|zip|Dir.glob("**/*")d
类classAprivatedeffooputs:fooendpublicdefbarputs:barendprivatedefzimputs:zimendprotecteddefdibputs:dibendendA的实例a=A.new测试a.foorescueputs:faila.barrescueputs:faila.zimrescueputs:faila.dibrescueputs:faila.gazrescueputs:fail测试输出failbarfailfailfail.发送测试[:foo,:bar,:zim,:dib,:gaz].each{|m|a.send(m)resc
很好奇,就使用rubyonrails自动化单元测试而言,你们正在做什么?您是否创建了一个脚本来在cron中运行rake作业并将结果邮寄给您?git中的预提交Hook?只是手动调用?我完全理解测试,但想知道在错误发生之前捕获错误的最佳实践是什么。让我们理所当然地认为测试本身是完美无缺的,并且可以正常工作。下一步是什么以确保他们在正确的时间将可能有害的结果传达给您? 最佳答案 不确定您到底想听什么,但是有几个级别的自动代码库控制:在处理某项功能时,您可以使用类似autotest的内容获得关于哪些有效,哪些无效的即时反馈。要确保您的提
假设我做了一个模块如下:m=Module.newdoclassCendend三个问题:除了对m的引用之外,还有什么方法可以访问C和m中的其他内容?我可以在创建匿名模块后为其命名吗(就像我输入“module...”一样)?如何在使用完匿名模块后将其删除,使其定义的常量不再存在? 最佳答案 三个答案:是的,使用ObjectSpace.此代码使c引用你的类(class)C不引用m:c=nilObjectSpace.each_object{|obj|c=objif(Class===objandobj.name=~/::C$/)}当然这取决于
我正在尝试使用ruby和Savon来使用网络服务。测试服务为http://www.webservicex.net/WS/WSDetails.aspx?WSID=9&CATID=2require'rubygems'require'savon'client=Savon::Client.new"http://www.webservicex.net/stockquote.asmx?WSDL"client.get_quotedo|soap|soap.body={:symbol=>"AAPL"}end返回SOAP异常。检查soap信封,在我看来soap请求没有正确的命名空间。任何人都可以建议我
我正在尝试设置一个puppet节点,但rubygems似乎不正常。如果我通过它自己的二进制文件(/usr/lib/ruby/gems/1.8/gems/facter-1.5.8/bin/facter)在cli上运行facter,它工作正常,但如果我通过由rubygems(/usr/bin/facter)安装的二进制文件,它抛出:/usr/lib/ruby/1.8/facter/uptime.rb:11:undefinedmethod`get_uptime'forFacter::Util::Uptime:Module(NoMethodError)from/usr/lib/ruby
关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。
我在我的项目目录中完成了compasscreate.和compassinitrails。几个问题:我已将我的.sass文件放在public/stylesheets中。这是放置它们的正确位置吗?当我运行compasswatch时,它不会自动编译这些.sass文件。我必须手动指定文件:compasswatchpublic/stylesheets/myfile.sass等。如何让它自动运行?文件ie.css、print.css和screen.css已放在stylesheets/compiled。如何在编译后不让它们重新出现的情况下删除它们?我自己编译的.sass文件编译成compiled/t