java 6 怎么用optional

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了java 6 怎么用optional相关的知识,希望对你有一定的参考价值。

参考技术A

Optional 是 JAVA8 新增的特性, 题住 标题写错了吗?



我们知道 Java 8 增加了一些很有用的 API, 其中一个就是 Optional. 如果对它不稍假探索, 只是轻描淡写的认为它可以优雅的解决 NullPointException 的问题, 于是代码就开始这么写了

Optional<User> user = ……
if (user.isPresent()) 
return user.getOrders();
 else 
return Collections.emptyList();

那么不得不说我们的思维仍然是在原地踏步, 只是本能的认为它不过是 User 实例的包装, 这与我们之前写成

User user = …..
if (user != null) 
return user.getOrders();
 else 
return Collections.emptyList();

实质上是没有任何分别. 这就是我们将要讲到的使用好 Java 8 Optional 类型的正确姿势.

在里约奥运之时, 新闻一再提起五星红旗有问题, 可是我怎么看都看不出来有什么问题, 后来才道是小星星膜拜中央的姿势不对. 因此我们千万也别对自己习以为常的事情觉得理所当然, 丝毫不会觉得有何不妥, 换句话说也就是当我们切换到 Java 8 的 Optional 时, 不能继承性的对待过往 null 时的那种思维, 应该掌握好新的, 正确的使用 Java 8 Optional 的正确姿势.

直白的讲, 当我们还在以如下几种方式使用 Optional 时, 就得开始检视自己了

调用 isPresent() 方法时

调用 get() 方法时

Optional 类型作为类/实例属性时

Optional 类型作为方法参数时

isPresent() 与 obj != null 无任何分别, 我们的生活依然在步步惊心. 而没有 isPresent() 作铺垫的 get() 调用在 IntelliJ IDEA 中会收到告警


Reports calls to java.util.Optional.get() without first checking with a isPresent() call if a value is available. If the Optional does not contain a value, get() will throw an exception. (调用 Optional.get() 前不事先用 isPresent() 检查值是否可用. 假如 Optional 不包含一个值, get() 将会抛出一个异常)


把 Optional 类型用作属性或是方法参数在 IntelliJ IDEA 中更是强力不推荐的


Reports any uses of java.util.Optional<T>, java.util.OptionalDouble, java.util.OptionalInt, java.util.OptionalLong or com.google.common.base.Optional as the type for a field or a parameter. Optional was designed to provide a limited mechanism for library method return types where there needed to be a clear way to represent “no result”. Using a field with type java.util.Optional is also problematic if the class needs to be Serializable, which java.util.Optional is not. (使用任何像 Optional 的类型作为字段或方法参数都是不可取的. Optional 只设计为类库方法的, 可明确表示可能无值情况下的返回类型. Optional 类型不可被序列化, 用作字段类型会出问题的)


所以 Optional 中我们真正可依赖的应该是除了 isPresent() 和 get() 的其他方法:

public<U> Optional<U> map(Function<? super T, ? extends U> mapper)

public T orElse(T other)

public T orElseGet(Supplier<? extends T> other)

public void ifPresent(Consumer<? super T> consumer)

public Optional<T> filter(Predicate<? super T> predicate)

public<U> Optional<U> flatMap(Function<? super T, Optional<U>> mapper)

public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X


我略有自信的按照它们大概使用频度对上面的方法排了一下序.

先又不得不提一下 Optional 的三种构造方式: Optional.of(obj),  Optional.ofNullable(obj) 和明确的 Optional.empty()

Optional.of(obj): 它要求传入的 obj 不能是 null 值的, 否则还没开始进入角色就倒在了NullPointerException 异常上了.

Optional.ofNullable(obj): 它以一种智能的, 宽容的方式来构造一个 Optional 实例. 来者不拒, 传 null 进到就得到 Optional.empty(), 非 null 就调用 Optional.of(obj).


那是不是我们只要用 Optional.ofNullable(obj) 一劳永逸, 以不变应二变的方式来构造 Optional 实例就行了呢? 那也未必, 否则 Optional.of(obj) 何必如此暴露呢, 私有则可?

我本人的观点是:  


1. 当我们非常非常的明确将要传给 Optional.of(obj) 的 obj 参数不可能为 null 时, 比如它是一个刚 new 出来的对象(Optional.of(new User(...))), 或者是一个非 null 常量时;  
2. 当想为obj 断言不为 null 时, 即我们想在万一 obj 为 null 立即报告 NullPointException 异常, 立即修改, 而不是隐藏空指针异常时, 我们就应该果断的用 Optional.of(obj) 来构造 Optional 实例, 而不让任何不可预计的 null 值有可乘之机隐身于 Optional 中.


现在才开始怎么去使用一个已有的 Optional 实例, 假定我们有一个实例 Optional<User> user, 下面是几个普遍的, 应避免 if(user.isPresent()) ... else ...  几中应用方式.

存在即返回, 无则提供默认值


   

return user.orElse(null);  //而不是 return user.isPresent() ? user.get() : null;

return user.orElse(UNKNOWN_USER);

   


存在即返回, 无则由函数来产生


   

return user.orElseGet(() -> fetchAUserFromDatabase()); //而不要 return user.isPresent() ? user: fetchAUserFromDatabase();

   


存在才对它做点什么


   

user.ifPresent(System.out::println);

 

//而不要下边那样

if (user.isPresent()) 

  System.out.println(user.get());

   


map 函数隆重登场

当 user.isPresent() 为真, 获得它关联的 orders, 为假则返回一个空集合时, 我们用上面的 orElse,orElseGet 方法都乏力时, 那原本就是 map 函数的责任, 我们可以这样一行


   
return user.map(u -> u.getOrders()).orElse(Collections.emptyList())
 
//上面避免了我们类似 Java 8 之前的做法
if(user.isPresent()) 
  return user.get().getOrders();
 else 
  return Collections.emptyList();

   


map  是可能无限级联的, 比如再深一层, 获得用户名的大写形式

   

return user.map(u -> u.getUsername())
           .map(name -> name.toUpperCase())
           .orElse(null);


这要搁在以前, 每一级调用的展开都需要放一个 null 值的判断


   


User user = .....
if(user != null) 
  String name = user.getUsername();
  if(name != null) 
    return name.toUpperCase();
   else 
    return null;
  
 else 
  return null;

   


针对这方面 Groovy 提供了一种安全的属性/方法访问操作符 ?.


   

user.getUsername().toUpperCase();

   


Swift 也有类似的语法, 只作用在  Optional 的类型上.

用了 isPresent() 处理 NullPointerException 不叫优雅, 有了  orElse, orElseGet 等, 特别是 map 方法才叫优雅.

其他几个, filter() 把不符合条件的值变为 empty(),  flatMap() 总是与 map() 方法成对的,  orElseThrow() 在有值时直接返回, 无值时抛出想要的异常.

一句话小结: 使用 Optional 时尽量不直接调用 Optional.get() 方法, Optional.isPresent() 更应该被视为一个私有方法, 应依赖于其他像 Optional.orElse(), Optional.orElseGet(),Optional.map() 等这样的方法.

最后, 最好的理解 Java 8 Optional 的方法莫过于看它的源代码 java.util.Optional, 阅读了源代码才能真真正正的让你解释起来最有底气, Optional 的方法中基本都是内部调用  isPresent() 判断, 真时处理值, 假时什么也不做.

本回答被提问者和网友采纳

Spring Data JPA findOne() 改为 Optional 怎么用?

【中文标题】Spring Data JPA findOne() 改为 Optional 怎么用?【英文标题】:Spring Data JPA findOne() change to Optional how to use this? 【发布时间】:2018-08-25 06:50:38 【问题描述】:

我正在学习 SpringBoot2.0Java8

并且我遵循了一些博客制作教程示例。

教程源码为:

@GetMapping("/id/edit")
public String edit(@PathVariable Long id, Model model) 
  model.addAttribute("categoryDto", categoryService.findOne(id));
  return "category/edit";

但是这段代码抛出了这个错误:

categoryService.findOne(id)

我正在考虑将 JPA 的 findOne() 方法更改为 Optional&lt; S &gt;

如何解决?

更多信息:

这是 categoryService 方法:

public Category findOne(Long id) 
  return categoryRepository.findOne(id);

【问题讨论】:

1.你能把链接放到你关注的博客上吗,或者你能显示完整的代码吗? categoryService 是如何声明的? 2. 你得到的异常是什么 【参考方案1】:

至少从2.0版本,Spring-Data-Jpa修改findOne()。 现在,findOne() 既没有相同的签名也没有相同的行为。 之前在CrudRepository接口中定义为:

T findOne(ID primaryKey);

现在,您将在 CrudRepository 中找到的单个 findOne() 方法在 QueryByExampleExecutor 接口中定义为:

<S extends T> Optional<S> findOne(Example<S> example);

最终由SimpleJpaRepository 实现,CrudRepository 接口的默认实现。 此方法是通过示例搜索进行查询,您不希望将其作为替代方法。

其实在新的API中还是有同样行为的方法,只是方法名变了。 在CrudRepository接口中,它从findOne()重命名为findById()

Optional<T> findById(ID id); 

现在它返回一个Optional,这对于阻止NullPointerException 来说还不错。

所以,现在实际调用的方法是Optional&lt;T&gt; findById(ID id)

如何使用它? 学习Optional 用法。 以下是有关其规范的重要信息:

一个容器对象,它可能包含也可能不包含非空值。如果一个 值存在,isPresent() 将返回 true,get() 将返回 价值。

其他方法取决于是否存在 提供了包含的值,例如 orElse() (返回默认值 如果值不存在)和 ifPresent()(如果 值存在)。


关于如何使用OptionalOptional&lt;T&gt; findById(ID id) 的一些提示。

通常,当您通过 id 查找实体时,您希望将其返回或在未检索到时进行特定处理。

以下是三个经典用法示例。

    假设如果找到实体,您想要获取它,否则您想要获取默认值。

你可以写:

Foo foo = repository.findById(id)
                    .orElse(new Foo());

或在有意义的情况下获取 null 默认值(与 API 更改之前的行为相同):

Foo foo = repository.findById(id)
                    .orElse(null);
    假设如果实体被发现你想返回它,否则你想抛出一个异常。

你可以写:

return repository.findById(id)
        .orElseThrow(() -> new EntityNotFoundException(id));
    假设您想根据是否找到实体应用不同的处理(不一定抛出异常)。

你可以写:

Optional<Foo> fooOptional = fooRepository.findById(id);
if (fooOptional.isPresent()) 
    Foo foo = fooOptional.get();
    // processing with foo ...
 else 
    // alternative processing....

【讨论】:

thx david :) NotFoundEntity is extends RuntimeException super("could not found " + id );像这样? 第一部分是错误的:该方法没有被移动到QueryByExampleExecutor,它只是被重命名为findById(…),返回一个Optional。此外,始终在Optional 上使用….map(…),切勿致电get() 谢谢 davidxxx。你救了我 也许 EntityNotFoundException 而不是 NotFoundEntity 更好。 @zhouji 同意并更新。此外,它在javax.persistence API 中定义为RuntimeException。所以真的很有意义。【参考方案2】:

该方法已重命名为 findById(…) 返回一个 Optional,因此您必须自己处理缺勤:

Optional<Foo> result = repository.findById(…);

result.ifPresent(it -> …); // do something with the value if present
result.map(it -> …); // map the value if present
Foo foo = result.orElse(null); // if you want to continue just like before

【讨论】:

如何考虑 return repository.findById(id).orElseThrow(() -> new NotFoundEntity(id)); 这家伙说这是错误的:tuhrig.de/anti-pattern-dont-use-optionals-for-data-repositories 显然有人错了——或者至少不是很微妙——在互联网上。 ¯\_(ツ)_/¯【参考方案3】:

确实,在最新版本的 Spring Data 中,findOne 返回了一个可选项。如果要从 Optional 中检索对象,只需在 Optional 上使用 get()。首先,存储库应该将可选项返回给服务,然后由服务处理可选项为空的情况。之后,服务应该将对象返回给控制器。

【讨论】:

建议在Optional 上致电….get() 是个坏建议。 Optional 有专门的方法来处理值。 确实,为了处理值,还有更好的方法,比如.orElse()或者.orElseThrow()。但是对于简单地检索项目而不检查存储库调用是否返回所需的对象,.get() 应该只是这样做【参考方案4】:

我总是在广泛使用的 CrudRepository 存储库/接口中编写默认方法 “findByIdOrError”

@Repository 
public interface RequestRepository extends CrudRepository<Request, Integer> 

    default Request findByIdOrError(Integer id) 
        return findById(id).orElseThrow(EntityNotFoundException::new);
     

【讨论】:

但这会返回一个 Request 对象。我应该如何将其转换为我正在寻找的实体(如果存在)?【参考方案5】:

Optional api 提供了获取值的方法。您可以检查isPresent() 是否存在该值,然后调用get(),或者您可以调用与orElse() 链接的get() 并提供默认值。

您可以尝试做的最后一件事是在自定义方法上使用@Query()

【讨论】:

建议在Optional 上致电….get() 是个坏建议。 Optional 有专门的方法来处理这个值。【参考方案6】:

考虑一个 User 实体和 UserRepository。在如下服务包代码中。

Optional<User> resultUser = UserRepository.findById(userId);  //return Optional

User createdUser = resultUser.get();  //return User

现在您可以使用 getter 访问所有用户实体属性。

createdUser.getId(); 
createdUser.getName();

这样。

【讨论】:

【参考方案7】:

自 Spring Data Commons 2.0 版起,CrudRepository interfacefindOne 方法已被 findById 取代。 你将findOne(id)替换为:

findById(id).orElse(null)

【讨论】:

以上是关于java 6 怎么用optional的主要内容,如果未能解决你的问题,请参考以下文章

请问用java从1-33个整数中随机抽取6个数字 且不重复 该怎么写?

怎么用java打印图形三: 1 7 12 16 19 21 2 8 13 17 20 3 9 14 18 4 10 15 5 11 6

Java语言里面大括号怎么用?

用Java排序元素

怎么用Java 编写九九乘法表?要效果是这样的 1*1=1 2*2=4 2*1=2 3*3=9 3*2=6 3*1=3

java连接Oracle数据库,报错软件包oracle.jdbc不存在,下了个ojdbc6_g.jar不知道怎么用,望高手指教。