Optional 函数式接口
文章目录
- 1、Optional
- 1 概述
- 2 使用
- 2.1 创建对象
- 2.2 安全消费值
- 2.3 获取值
- 2.4 安全获取值
- 2.5 过滤
- 2.6 判断
- 2.7 数据转换
- 2、函数式接口
- 1 概述
- 2 常见函数式接口
1、Optional
1 概述
我们在编写代码的时候出现最多的就是空指针异常。所以在很多情况下我们需要做各种非空的判断。例如:
package com.example.optionaldemo01.domain;
import java.util.List;
/**
* @Author Js
* @Description
* @Date 2024-11-10 14:27
* @Version 1.0
**/
public class Author {
private Long id;
private String name;
private int age;
private String intro;
private List<Books> books;
@Override
public String toString() {
return "Author{" +
"id=" + id +
", name='" + name + '\'' +
", age=" + age +
", intro='" + intro + '\'' +
", books=" + books +
'}';
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getIntro() {
return intro;
}
public void setIntro(String intro) {
this.intro = intro;
}
public List<Books> getBooks() {
return books;
}
public void setBooks(List<Books> books) {
this.books = books;
}
public Author(Long id, String name, int age, String intro, List<Books> books) {
this.id = id;
this.name = name;
this.age = age;
this.intro = intro;
this.books = books;
}
}
package com.example.optionaldemo01.domain;
/**
* @Author Js
* @Description
* @Date 2024-11-10 15:45
* @Version 1.0
**/
public class Books {
String name;
@Override
public String toString() {
return "Books{" +
"name='" + name + '\'' +
'}';
}
public Books(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Author author = getAuthor();
if (author != null) {
System.out.println(author.getName());
}
尤其是对象中的属性还是一个对象的情况下。这种判断会更多。
而过多的判断语句会让我们的代码显得臃肿不堪。
所以在JDK8中引入了Optional,养成使用Optional的习惯后你可以写出更优雅的代码来避免空指针异常。
并且在很多函数式编程相关的API中也都用到了Optional,如果不会使用Optional也会对函数式编程的学习造成影响。
2 使用
2.1 创建对象
Optional就好像是包装类,可以把我们的具体数据封装0ptional对象内部。然后我们去使用Optional中封装好的方法操作封装进去的数据就可以非常优雅的避免空指针异常。
我们一般使用optional的静态方法ofNullable来把数据封装成一个0ptional对象。无论传入的参数是否为nul都不会出现问题。
Author author = getAuthor();
Optional<Author> authoroptional = Optional.ofNullable(author):
你可能会觉得还要加一行代码来封装数据比较麻烦。但是如果改造下getAuthor方法,让其的返回值就是封装好的0ptional的话,我们在使用时就会方便很多。
而且在实际开发中我们的数据很多是从数据库获取的。Mybatis从3.5版本可以也已经支持Optional了。我们可以直接把dao方法的返回值类型定义成Optional类型,MyBastis会自己把数据封装成Optional对象返回。封装的过程也不需要我们自己操作。
如果你确定一个对象不是空的则可以使用Optional的静态方法of来把数据封装成Optional对象。
Author author new Author();
Optional<Author> authoroptional = Optional.of(author):
但是一定要注意,如果使用of的时候传入的参数必须不为null。(尝试下传入null会出现什么结果)
如果一个方法的返回值类型是Optional类型。而如果我们经判断发现某次计算得到的返回值为null,这个时候就需要把null封装成Optional对象返回。这时则可以使用0ptional的静态方法empty来进行封装。
optional.empty()
所以最后你觉得哪种方式会更方便呢?如果你看了ofNullable底层实现,就会发现ofNullable底层就是使用二三两种方法结合的,所以推荐款第一种方法
2.2 安全消费值
我们获取到一个Optional对象后肯定需要对其中的数据进行使用。这时候我们可以使用其 ifPresent 方法对来消费其中的值。
这个方法会判断其内封装的数据是否为空,不为空时才会执行具体的消费代码。
这样使用起来就更加安全了。例如,以下写法就优雅的避免了空指针异常。
例如,以下写法就优雅的避免了空指针异常
package com.example.optionaldemo01;
import com.example.optionaldemo01.domain.Author;
import java.util.Optional;
/**
* @Author Js
* @Description
* @Date 2024-11-10 14:51
* @Version 1.0
**/
public class main {
public static void main(String[] args) {
Optional<Author> authorOptional = Optional.ofNullable(getAuthor());
authorOptional.ifPresent(author -> System.out.println(author.getName()));
}
public static Author getAuthor(){
Author author = new Author(1L, "张三", 18, "多姿多彩的少年");
return author;
}
}
2.3 获取值
如果我们想获取值自己进行处理可以使用get方法获取,但是不推荐。因为当Optional内部的数据为空的时候会出现异常,
- 对象有值
public class main {
public static void main(String[] args) {
Optional<Author> authorOptional = getAuthor();
Author author = authorOptional.get();
}
public static Optional<Author> getAuthor(){
Author author = new Author(1L, "张三", 18, "多姿多彩的少年");
return Optional.ofNullable(author);
}
}
- 对象为null
public class main {
public static void main(String[] args) {
Optional<Author> authorOptional = getAuthor();
Author author = authorOptional.get();
}
public static Optional<Author> getAuthor(){
Author author = new Author(1L, "张三", 18, "多姿多彩的少年");
return Optional.ofNullable(null);
}
}
此时会报错:
Exception in thread "main" java.util.NoSuchElementException: No value present
at java.base/java.util.Optional.get(Optional.java:143)
at com.example.optionaldemo01.main.main(main.java:19)
Process finished with exit code 1
2.4 安全获取值
如果我们期望安全的获取值。我们不推荐使用get方法,而是使用Optional提供的以下方法。
- orElseGet
获取数据并目设置数据为空时的默认值,如果数据不为空就能获取到该数据。如果为空则根据你传入的参数来创建对象作为默认值返回。
public class main {
public static void main(String[] args) {
Optional<Author> authorOptional = getAuthor();
Author author = authorOptional.orElseGet(new Supplier<Author>() {
@Override
public Author get() {
//获取数据并目设置数据为空时的默认值
return new Author(2L, "王五", 19, "不讲武德的少年");
}
});
System.out.println(author);
//使用lambda表达式
// Optional<Author> authorOptional = getAuthor();
// Author author = authorOptional.orElseGet(() ->
// new Author(2L, "王五", 19, "不讲武德的少年")
// );
// System.out.println(author);
}
public static Optional<Author> getAuthor() {
Author author = new Author(1L, "张三", 18, "多姿多彩的少年");
return Optional.ofNullable(null);
}
}
- orElseThrow
获取数据,如果数据不为空就能获取到该数据。如果为空则根据你传入的参数来创建异常抛出。
public class main {
public static void main(String[] args) {
Optional<Author> authorOptional = getAuthor();
try {
Author author = authorOptional.orElseThrow(() -> new RuntimeException("author为空"));
System.out.println(author);
} catch (Throwable e) {
throw new RuntimeException(e);
}
}
public static Optional<Author> getAuthor() {
Author author = new Author(1L, "张三", 18, "多姿多彩的少年");
return Optional.ofNullable(null);
}
}
2.5 过滤
我们可以使用fiter方法对数据进行过滤。如果原本是有数据的,但是不符合判断,也会变成一个无数据的Optional对象。
public class main {
public static void main(String[] args) {
Optional<Author> authorOptionals = getAuthor();
// Optional<Author> authors = authorOptionals.filter(new Predicate<Author>() {
// @Override
// public boolean test(Author author) {
// return author.getAge()>16;
// }
//
// });
//lambda表达式
Optional<Author> authors = authorOptionals.filter(author -> author.getAge()>16);
}
public static Optional<Author> getAuthor() {
Author author = new Author(1L, "张三", 18, "多姿多彩的少年1");
return Optional.ofNullable(author);
}
}
2.6 判断
我们可以使用isPresent方法进行是否存在数据的判断。如果为空返回值为false,如果不为空,返回值为true。但是这种方式并不能体现Optional的好处,更推荐使用ifPresent方法。
public class main {
public static void main(String[] args) {
Optional<Author> authorOptional = Optional.ofNullable(getAuthor());
authorOptional.ifPresent(author -> System.out.println(author));
}
public static Author getAuthor() {
List<Books> books = Arrays.asList(
new Books("红楼梦"),
new Books("西游记"),
new Books("水浒传"),
new Books("三国演义"));
Author author = new Author(1L, "张三", 18, "多姿多彩的少年1",books);
return author;
}
}
2.7 数据转换
Optional还提供了map可以让我们的对数据进行转换,并且转换得到的数据也还是被Optional包装好的,保证了我们的使用安全,。
例如我们想获取作家的书籍集合。
public class main {
public static void main(String[] args) {
Optional<Author> authoroptional = Optional.ofNullable(getAuthor());
Optional<List<Books>> books = authoroptional.map(author -> author.getBooks());
books.ifPresent(books1 -> {
for (Books book : books1) {
System.out.println(book.getName());
}
});
}
public static Author getAuthor() {
List<Books> books = Arrays.asList(
new Books("红楼梦"),
new Books("西游记"),
new Books("水浒传"),
new Books("三国演义"));
Author author = new Author(1L, "张三", 18, "多姿多彩的少年1",books);
return author;
}
}
2、函数式接口
1 概述
只有一个抽象方法的接口我们称之为函数接口。
JDK的函数式接口都加上了 @FunctionalInterface 注解进行标识。但是无论是否加上该注解只要接口中只有一个抽象方法,都是函数式接口。
2 常见函数式接口
-
Consumer 消费接口
- 根据其中抽象方法的参数列表和返回值类型知道,我们可以在方法中对传入的参数进行消费。
- Function 计算转换接口
- 根据其中抽象方法的参数列表和返回值类型知道,我们可以在方法中对传入的参数计算或转换,把结果返回
- Predicate 判断接口
- 根据其中抽象方法的参数列表和返回值类型知道,我们可以在方法中对传入的参数条件判断,返回判断结果