文章目录
  1. 1. Optional

Optional

  Java 8中的Optional是一个可以包含或不可以包含非空值的容器对象,在 Stream API中很多地方也都使用到了Optional。

  java中非常讨厌的一点就是nullpoint,碰到空指针就会出错抛Exception,然后需要逐行检查是哪个对象为空,带来大量的不必要精力损耗,抛出NPE错误不是用户操作的错误,而是开发人员的错误,应该被避免,那么只能在每个方法中加入非空检查,阅读性和维护性都比较差。

  如下面这个代码的手工非空检查:
public void addAddressToCustomer(Customer customer, Address newAddress){
if ( customer == null || newAddress == null)
return;

if ( customer.getAddresses() == null ){
customer.setAddresses ( new ArrayList<>());
}
customer.addAddress(newAddress);
}

  另外还有一些开发人员喜欢通过非空检查来实现业务逻辑,空对象不应该用来决定系统的行为,它们是意外的Exceptional值,应当被看成是错误,而不是业务逻辑状态。

  当我们一个方法返回List集合时,应该总是返回一个空的List,而不是Null,这就允许调用者能够遍历它而不必检查Null,否则就抛出NPE。

  但是如果我们根据标识键ID查询数据库,没有查到,需要返回一个空对象怎么办?有人建议抛出Exception,其实这不符合函数方法一进一出的原则,变成一个函数方法有两个返回,一个是正常返回,一个出错Exception,函数式编程范式告诫我们不要轻易抛Exception。

  这时Java 8的Optional就发挥作用了,允许我们返回一个空的对象。

  Optional有方法 isPresent() 和 get() 是用来检查其包含的对象是否为空或不是,然后返回它,如:

Optional someValue = someMethod();

if (someValue.isPresent()) { // check

someValue.get().someOtherMethod(); // retrieve and call

}

  但是这种用法并不能体现Java 8的全部好处,你可以将Optional看成是需要使用某个T值的方法之间某种中间人或者协调者Mediator,而不只是一个普通对象的包装器。

  如果你有一个值返回类型T,你有一个方法需要使用这个值,那么你可以让 Optional 处于中间,确保它们之间交互进行,而不必要人工干预。

  这样,协调者Optional能够照顾T的值提供给你的方法作为输入参数,在这种情况下,如果T是空,可以确保不会出错,这样在T值为空时也可以让一切都正常运作,你也可以让Optional执行其他动作,如执行一段代码块等等,这样它就实际上是语言机制的很好的补充。

  下面这个案例涉及到Lambda表达式 方法引用,是将单词流中第一个以”L”开始单词取出,作为返回结果是一个Optional
使用ifPresent()

  这个案例的代码如下:

Stream names = Stream.of(“Lamurudu”, “Okanbi”, “Oduduwa”);

Optional longest = names

.filter(name -> name.startsWith("L"))

.findFirst();

longest.ifPresent(name -> {

    String s = name.toUpperCase();

    System.out.println("The longest name is "+ s);

});

  这里ifPresent() 是将一个Lambda表达式作为输入,T值如果不为空将传入这个lambda。那么这个lambda将不为空的单词转为大写输出显示。在前面names单词流寻找结果中,有可能找不到开始字母为L的单词,返回为空,也可能找到不为空,这两种情况都传入lambda中,无需我们打开盒子自己编写代码来判断,它自动帮助我们完成了,无需人工干预。

使用map()

  如果你想从Optional中返回一个值怎么办?使用 map(),如下:

Stream names = Stream.of(“Lamurudu”, “Okanbi”, “Oduduwa”);

Optional longest = names

.filter(name -> name.startsWith("L"))

.findFirst();

Optional lNameInCaps = longest.map(String::toUpperCase);

  使用Optional的map方法能够返回另外一个Optional,如上面的 LnameInCaps,因为传入map()的参数值也许会导致一个空值。

使用orElse()

  如果在T可能空时你需要一个值的话,那么可以使用 orElse(),它能在T值存在的情况下返回这个值,否则返回输入值。

Stream names = Stream.of(“Lamurudu”, “Okanbi”, “Oduduwa”);

Optional longest = names

.filter(name -> name.startsWith("Q"))

.findFirst();

String alternate = longest.orElse(“Nimrod”);

System.out.println(alternate); //prints out “Nimrod”

使用orElseGet()

  orElseGet() 方法类似于orElse(),但是不是直接返回输入参数,而是调用输入参数,返回调用的结果,这个输入参数通常是lambda:

Stream names = Stream.of(“Lamurudu”, “Okanbi”, “Oduduwa”);

Optional longest = names

.filter(name -> name.startsWith("Q"))

.findFirst();

String alternate = longest.orElseGet(() -> {

// perform some interesting code operation

// then return the alternate value.

return "Nimrod";

});

System.out.println(alternate);

使用 orElseThrow()

  orElseThrow()是在当遭遇Null时,决定抛出哪个Exception时使用:

Stream names = Stream.of(“Lamurudu”, “Okanbi”, “Oduduwa”);

Optional longest = names

.filter(name -> name.startsWith("Q"))

.findFirst();

longest.orElseThrow(NoSuchElementStartingWithQException::new);

总结,你能创建下面三种类型的Optional

Optional getSomeValue() {

// 返回一个空的Optional类型;

return Optional.empty();

}

Optional getSomeValue() {

SomeType value = …;

// 使用这个方法,值不可以为空,否则抛exception

return Optional.of(value);

}

Optional getSomeValue() {

SomeType value = …;

// 使用这个方法,值可以为空,如果为空返回Optional.empty

return Optional.ofNullable(value);

// usage

Optional someType = getSomeValue();

首先创建一个函数接口ResultSetProcessor :

@FunctionalInterface
public interface ResultSetProcessor {

public void process(ResultSet resultSet,
                    long currentRow)
                    throws SQLException;

}

下面做个简单查询案例,使用这个接口遍历

public static void select(Connection connection,
String sql,
ResultSetProcessor processor,
Object… params) {
try (PreparedStatement ps = connection.prepareStatement(sql)) {
int cnt = 0;
for (Object param : params) {
ps.setObject(++cnt, param));
}
try (ResultSet rs = ps.executeQuery()) {
long rowCnt = 0;
while (rs.next()) {
processor.process(rs, rowCnt++);
}
} catch (SQLException e) {
throw new DataAccessException(e);
}
} catch (SQLException e) {
throw new DataAccessException(e);
}
}

调用这个select语句如下:

select(connection, “select * from MY_TABLE”,(rs, cnt)-> {
System.out.println(rs.getInt(1)+” “+cnt)
});

select的第三个参数ResultSetProcessor这是个函数,所以我们传入的是一个匿名函数。
Streams API

   java 8提供了更强大的 Streams API,我们可以对ResultSet处理更加强大。创建一个自己的Tuple 类型,代表ResultSet中一行记录。

   下面我们将一个查询和ResultSet包装在一个Iterator中:

public class ResultSetIterator implements Iterator {

private ResultSet rs;
private PreparedStatement ps;
private Connection connection;
private String sql;

public ResultSetIterator(Connection connection, String sql) {
    assert connection != null;
    assert sql != null;
    this.connection = connection;
    this.sql = sql;
}

public void init() {
    try {
        ps = connection.prepareStatement(sql);
        rs = ps.executeQuery();

    } catch (SQLException e) {
        close();
        throw new DataAccessException(e);
    }
}

@Override
public boolean hasNext() {
    if (ps == null) {
        init();
    }
    try {
        boolean hasMore = rs.next();
        if (!hasMore) {
            close();
        }
        return hasMore;
    } catch (SQLException e) {
        close();
        throw new DataAccessException(e);
    }

}

private void close() {
    try {
        rs.close();
        try {
            ps.close();
        } catch (SQLException e) {
            //nothing we can do here
        }
    } catch (SQLException e) {
        //nothing we can do here
    }
}

@Override
public Tuple next() {
    try {
        return SQL.rowAsTuple(sql, rs);
    } catch (DataAccessException e) {
        close();
        throw e;
    }
}

}

这是一个遍历器,每次返回ResultSet的一行记录,返回类型是我们定义Tuple.

关于tuple定义可见源码。

我们和Stream绑定在一起如下:

public static Stream stream(final Connection connection,
final String sql,
final Object… parms) {
return StreamSupport
.stream(Spliterators.spliteratorUnknownSize(
new ResultSetIterator(connection, sql), 0), false);
}

Java 8 提供StreamSupport静态方法.stream来创建 java.util.stream.Stream实例,同时还需要java.util.stream.Spliterator,这是一个用来遍历和分区一个序列元素(集合)的特殊类型,有了它才能并行处理我们饿操作,而Spliterators 这是能够对已经存在的集合如java.util.Iterator.提供并行操作。

我们调用上面stream如下:

long result = stream(connection, “select TEST_ID from TEST_TABLE”)
.filter((t) -> t.asInt(“TEST_ID”) % 2 == 0)
.limit(100)
.count();

这是查询所有的TEST_ID,然后过滤掉所有非偶数,最后再运行一个计数。非常简单明了。如果你使用ORM/JPA等框架,可能无法让自己的SQL代码如此优雅直接了,它类似Hibernate的 criteria。

https://github.com/jexenberger/lambda-tuples

java8

  1. 实现Runnable线程案例

使用() -> {} 替代匿名类:

//Before Java 8:
new Thread(new Runnable() {
@Override
public void run() {
System.out.println(“Before Java8 “);
}
}).start();

//Java 8 way:
new Thread( () -> System.out.println(“In Java8!”) ).start();

Output:
too much code, for too little to do
Lambda expression rocks !!

你可以使用 下面语法实现Lambda:

(params) -> expression
(params) -> statement
(params) -> { statements }

如果你的方法并不改变任何方法参数,比如只是输出,那么可以简写如下:
() -> System.out.println(“Hello Lambda Expressions”);

如果你的方法接受两个方法参数,如下:

(int even, int odd) -> even + odd

2.实现事件处理

如果你曾经做过Swing 编程,你将永远不会忘记编写事件侦听器代码。使用lambda表达式如下所示写出更好的事件侦听器的代码。

// Before Java 8:
JButton show = new JButton(“Show”);
show.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
System.out.println(“without lambda expression is boring”);
}
});

// Java 8 way:
show.addActionListener((e) -> {
System.out.println(“Action !! Lambda expressions Rocks”);
});

在java 8中你可以使用Lambda表达式替代丑陋的匿名类。

3.使用Lambda表达式遍历List集合

//Prior Java 8 :
List features = Arrays.asList(“Lambdas”, “Default Method”,
“Stream API”, “Date and Time API”);
for (String feature : features) {
System.out.println(feature);
}

//In Java 8:
List features = Arrays.asList(“Lambdas”, “Default Method”, “Stream API”,
“Date and Time API”);
features.forEach(n -> System.out.println(n));

// Even better use Method reference feature of Java 8
// method reference is denoted by :: (double colon) operator
// looks similar to score resolution operator of C++
features.forEach(System.out::println);

Output:
Lambdas
Default Method
Stream API
Date and Time API

方法引用是使用两个冒号::这个操作符号。
4.使用Lambda表达式和函数接口

为了支持函数编程,Java 8加入了一个新的包java.util.function,其中有一个接口java.util.function.Predicate是支持Lambda函数编程:

public static void main(args[]){
List languages = Arrays.asList(“Java”, “Scala”, “C++”, “Haskell”, “Lisp”);

System.out.println(“Languages which starts with J :”);
filter(languages, (str)->str.startsWith(“J”));

System.out.println(“Languages which ends with a “);
filter(languages, (str)->str.endsWith(“a”));

System.out.println(“Print all languages :”);
filter(languages, (str)->true);

System.out.println(“Print no language : “);
filter(languages, (str)->false);

System.out.println(“Print language whose length greater than 4:”);
filter(languages, (str)->str.length() > 4);
}

public static void filter(List names, Predicate condition) {
for(String name: names) {
if(condition.test(name)) {
System.out.println(name + “ “);
}
}
}
}

Output:
Languages which starts with J :
Java
Languages which ends with a
Java
Scala
Print all languages :
Java
Scala
C++
Haskell
Lisp
Print no language :
Print language whose length greater than 4:
Scala
Haskell

//Even better
public static void filter(List names, Predicate condition) {
names.stream().filter((name) -> (condition.test(name)))
.forEach((name) -> {System.out.println(name + “ “);
});
}

你能看到来自Stream API 的filter方法能够接受 Predicate参数, 能够允许测试多个条件。

5.复杂的结合Predicate 使用

java.util.function.Predicate提供and(), or() 和 xor()可以进行逻辑操作,比如为了得到一串字符串中以”J”开头的4个长度:

// We can even combine Predicate using and(), or() And xor() logical functions
// for example to find names, which starts with J and four letters long, you
// can pass combination of two Predicate
Predicate startsWithJ = (n) -> n.startsWith(“J”);
Predicate fourLetterLong = (n) -> n.length() == 4;

names.stream()
.filter(startsWithJ.and(fourLetterLong))
.forEach((n) -> System.out.print(“\nName, which starts with
‘J’ and four letter long is : “ + n));

其中startsWithJ.and(fourLetterLong)是使用了AND逻辑操作。

6.使用Lambda实现Map 和 Reduce

最流行的函数编程概念是map,它允许你改变你的对象,在这个案例中,我们将costBeforeTeax集合中每个元素改变了增加一定的数值,我们将Lambda表达式 x -> x*x传送map()方法,这将应用到stream中所有元素。然后我们使用 forEach() 打印出这个集合的元素.

// applying 12% VAT on each purchase
// Without lambda expressions:
List costBeforeTax = Arrays.asList(100, 200, 300, 400, 500);
for (Integer cost : costBeforeTax) {
double price = cost + .12*cost;
System.out.println(price);
}

// With Lambda expression:
List costBeforeTax = Arrays.asList(100, 200, 300, 400, 500);
costBeforeTax.stream().map((cost) -> cost + .12*cost)
.forEach(System.out::println);

Output
112.0
224.0
336.0
448.0
560.0
112.0
224.0
336.0
448.0
560.0

reduce() 是将集合中所有值结合进一个,Reduce类似SQL语句中的sum(), avg() 或count(),

// Applying 12% VAT on each purchase
// Old way:
List costBeforeTax = Arrays.asList(100, 200, 300, 400, 500);
double total = 0;
for (Integer cost : costBeforeTax) {
double price = cost + .12*cost;
total = total + price;

}
System.out.println(“Total : “ + total);

// New way:
List costBeforeTax = Arrays.asList(100, 200, 300, 400, 500);
double bill = costBeforeTax.stream().map((cost) -> cost + .12*cost)
.reduce((sum, cost) -> sum + cost)
.get();
System.out.println(“Total : “ + bill);

Output
Total : 1680.0
Total : 1680.0

7.通过filtering 创建一个字符串String的集合

Filtering是对大型Collection操作的一个通用操作,Stream提供filter()方法,接受一个Predicate对象,意味着你能传送lambda表达式作为一个过滤逻辑进入这个方法:

// Create a List with String more than 2 characters
List filtered = strList.stream().filter(x -> x.length()> 2)
.collect(Collectors.toList());
System.out.printf(“Original List : %s, filtered list : %s %n”,
strList, filtered);

Output :
Original List : [abc, , bcd, , defg, jk], filtered list : [abc, bcd, defg]

8.对集合中每个元素应用函数

我们经常需要对集合中元素运用一定的功能,如表中的每个元素乘以或除以一个值等等.

// Convert String to Uppercase and join them using coma
List G7 = Arrays.asList(“USA”, “Japan”, “France”, “Germany”,
“Italy”, “U.K.”,”Canada”);
String G7Countries = G7.stream().map(x -> x.toUpperCase())
.collect(Collectors.joining(“, “));
System.out.println(G7Countries);

Output :
USA, JAPAN, FRANCE, GERMANY, ITALY, U.K., CANADA

上面是将字符串转换为大写,然后使用逗号串起来。

9.通过复制不同的值创建一个子列表

使用Stream的distinct()方法过滤集合中重复元素。

// Create List of square of all distinct numbers
List numbers = Arrays.asList(9, 10, 3, 4, 7, 3, 4);
List distinct = numbers.stream().map( i -> i*i).distinct()
.collect(Collectors.toList());
System.out.printf(“Original List : %s, Square Without duplicates :
%s %n”, numbers, distinct);

Output :
Original List : [9, 10, 3, 4, 7, 3, 4], Square Without
duplicates : [81, 100, 9, 16, 49]

10.计算List中的元素的最大值,最小值,总和及平均值

//Get count, min, max, sum, and average for numbers
List primes = Arrays.asList(2, 3, 5, 7, 11, 13, 17, 19, 23, 29);
IntSummaryStatistics stats = primes.stream().mapToInt((x) -> x)
.summaryStatistics();
System.out.println(“Highest prime number in List : “ + stats.getMax());
System.out.println(“Lowest prime number in List : “ + stats.getMin());
System.out.println(“Sum of all prime numbers : “ + stats.getSum());
System.out.println(“Average of all prime numbers : “ + stats.getAverage());

Output :
Highest prime number in List : 29
Lowest prime number in List : 2
Sum of all prime numbers : 129
Average of all prime numbers : 12.9

在本教程中主要讲解Java 8新的函数式编程功能,熟悉这些新的 API:streams, 函数接口, map扩展和新的日期API。
接口的缺省方法

Java 8让我们能够增加非抽象方法实现到一个接口中, 使用default,这个特点就是 Extension Methods.

interface Formula {

double calculate(int a);
default double sqrt(int a) {
    return Math.sqrt(a);
}

}

接口 Formula 定义了一个默认方法sqrt. 该接口实现类只要完成接口中抽象方法calculate即可,而sqrt方法可以被外部使用。

ormula formula = new Formula() {

@Override
public double calculate(int a) {
    return sqrt(a * 100);
}

};

formula.calculate(100); // 100.0
formula.sqrt(16); // 4.0

代码中formula 实现类是一个匿名类,在Java 8中有很多好的方法实现这种单个方法的匿名类。
Lambda表达式

先看看传统Java的代码例子:

List names = Arrays.asList(“peter”, “anna”, “mike”, “xenia”);

Collections.sort(names, new Comparator() {

@Override
public int compare(String a, String b) {
    return b.compareTo(a);
}

});

静态方法Collections.sort接受一个集合List和一个比较器comparator,后者是为了对集合中元素排序,一般我们都是创建一个匿名的比较器对象传递集合中。

Java 8使用Lambda表达式替代这种匿名类。

Collections.sort(names, (String a, String b) -> {

return b.compareTo(a);

});

代码相对简短,还可以再短小些:

Collections.sort(names, (String a, String b) -> b.compareTo(a));

对于只有一个方法,你可以忽略{} 和 return语法,当然还可以再简短:

Collections.sort(names, (a, b) -> b.compareTo(a));

Java编译器会照顾你忽略了a和b的类型String。这称为语言的类型判断。

函数接口

Lambda表达式是一种函数语法,与Java的类型语言是两种不同性质的语法,如同南北两个不同方向,那么Java 8的Lambda表达式如何配合Java天生的类型系统呢?每个Lambda都对应一个给定的类型,主要是一个接口类型,也称为函数接口,只能包含一个抽象方法,每个类型的Lambda表达式与这个抽象方法匹配,因为默认default方法不是抽象方法,你可以在接口中自由增加自定义的default默认方法。

我们可以使用很多接口作为lambda表达式,只要这个接口只包含一个抽象方法,为了确保你的接口符合需要,你应当加入元注解 @FunctionalInterface. 编译器将会注意到这个元注解,如果你试图增加第二个抽象方法到接口中,它会抛出编译错误。

@FunctionalInterface

interface Converter {

T convert(F from);

}

Converter converter = (from) -> Integer.valueOf(from);
Integer converted = converter.convert(“123”);
System.out.println(converted); // 123

这段代码中,我们定义了接口Converter一个抽象方法,注意虽然使用了@FunctionalInterface ,但是不使用也是可以,那么这个接口中抽象方法就可以作为Lambda表达式使用,首先,我们定义了这个接口的实现:

Converter converter = (from) -> Integer.valueOf(from);

(from) -> Integer.valueOf(from)实际是抽象方法 T convert(F from)的实现具体细节,这里是将字符串转换为整数型。

然后,我们就可以直接调用这个接口:converter.convert(“123”)

方法和构造器的引用

上述代码如果使用静态方法引用static method references将会更加简化:

Converter converter = Integer::valueOf;
Integer converted = converter.convert(“123”);
System.out.println(converted); // 123

Java 8 使用符号 ::让你传递方法或构造器的引用,因为 Integer.valueOf是一个静态方法,其引用方式是 Integer::valueOf,我们还能引用一个对象的普通方法:

class Something {

String startsWith(String s) {

    return String.valueOf(s.charAt(0));

}

}

Something something = new Something();
Converter converter = something::startsWith;
String converted = converter.convert(“Java”);
System.out.println(converted); // “J”

这里引用的是类Something的方法startsWith,我们首先要创建这个类的实例,然后使用something::startsWith,这相当于实现了接口Converter的convert方法。不必像我们传统方式,通过Something implements Converter,然后在具体实现Converter的抽象方法convert。这样摆脱了子类对接口的依赖。

符号::也可以使用在构造器方法中:

class Person {

String firstName;
String lastName;
Person() {}

Person(String firstName, String lastName) {
    this.firstName = firstName;
    this.lastName = lastName;

}

}

下面是创建Person的工厂接口:

interface PersonFactory

{

P create(String firstName, String lastName);

}

有别于传统手工实现这个接口的方式,我们使用::将接口和实现粘合在一起。

PersonFactory personFactory = Person::new;
Person person = personFactory.create(“Peter”, “Parker”);

我们使用Person::new创建了一个指向Person构造器的引用,Java编译器将会自动挑选匹配PersonFactory.create方法签名的构造器。

Lambda作用域

从Lambda表达式访问外部变量非常类似匿名对象访问外部一样,匿名对象只能访问外部访问有final定义的变量。
访问本地变量:

final int num = 1;
Converter stringConverter = (from) -> String.valueOf(from + num);
stringConverter.convert(2);

这里(from) -> String.valueOf(from + num)表达式 访问了外部的变量num,区别于匿名类,这个变量不是必须加上final定义。下面也可以:

int num = 1;
Converter stringConverter = (from) -> String.valueOf(from + num);

这就是Lambda表达式比匿名类的好处。但是这不代表你可以修改num值,这里是隐式的final,下面语法是不可以的:

int num = 1;
Converter stringConverter = (from) -> String.valueOf(from + num);
num = 3;

一旦被写入了Lambda表达式的变量不能再被修改了。
访问字段和静态变量

正好和本地变量相反,我们在Lambda表达式中可以对实例的字段和静态变量进行读和写。无论这个字段或静态变量是否在lambda表达式中使用过。

class Lambda4 {

static int outerStaticNum;
int outerNum;

void testScopes() {

    Converter<Integer, String> stringConverter1 = (from) -> {
        outerNum = 23;
        return String.valueOf(from);
    };

    Converter<Integer, String> stringConverter2 = (from) -> {
        outerStaticNum = 72;
        return String.valueOf(from);
    };
}

}
访问接口的默认方法

还记得开始的formula案例吗?接口Formula定义了一个默认方法sqrt,它可以被匿名对象访问 ,但是不能被Lambda表达式访问。

默认方法不能在Lambda表达式中访问,下面代码不会编译通过:

Formula formula = (a) -> sqrt( a * 100);

前面谈了java8的lambda,现在来了解Java8的Monad,Monad是范畴理论中一个词语,但是就像我们吃披萨不用到意大利一样,不学习饶舌的范畴论我们也可以理解Monad。范畴论中是如此隐晦地定义:

a monad in X is just a monoid in the category of endofunctors of X。

翻译软件都不知道单词endofunctors 。了解了闭包 Lambda,我们基本可以理解Monad是一个个lambda串联起来,说得标准一些,如下:

A monad is a structure that puts a value in a computational context

Monad是一种结构,这种结构能够将一个值放入一个可计算的上下文中。因为Lambda是一个和上下文环境有关的表达式,所以,这里对Monad的上下文理解就比较容易。

  scala中的map就是一个monad,见:Scala入门之函数编程。下面我们看看在java8中如何自己实现一个monad。

M unit(A a);
M bind(M
ma, Function> f);

interface M {
 M map(Function f){
  return flatMap( x -> unit( f.apply(x) ) );//
 }
 M flatMap(Function> f);
}

这里的map定义了一个monad,将flatMap和Unit绑定组合(串联)在一起。

  下面看看monad的好处,假设有三个类,有关车的保险名称:

public class Person {
 private Car car;
 public Car getCar() { return car; }
}
public class Car {
 private Insurance insurance;
 public Insurance getInsurance() { return insurance; }
}
public class Insurance {
 private String name;
 public String getName() { return name; }
}

这里Insurance是保险,里面有保险名字,为了得到某个人的车辆的保险名字,需要下面:

String getCarInsuranceName(Person person) {
 if (person != null) {
  Car car = person.getCar();
  if (car != null) {
   Insurance insurance = car.getInsurance;
   if (insurance != null) {
    return insurance.getName()
   }
  }
 }
 return “Unknown”;
}

需要经过很深入嵌套式的判断然后才能返回。

  我们首先定义一个Optional :

public class Optional {
 private static final Optional<?> EMPTY = new Optional<>(null);
 private final T value;
 private Optional(T value) {
  this.value = value;
 }
 public Optional map(Function<? super T, ? extends U> f) {
  return value == null ? EMPTY : new Optional(f.apply(value));
 }
 public Optional flatMap(Function<? super T, Optional> f) {
  return value == null ? EMPTY : f.apply(value);
 }
}

然后对前面三个模型重新编写,加入Optional:

public class Person {
 private Optional car;
 public Optional getCar() { return car; }
}
public class Car {
 private Optional insurance;
 public Optional getInsurance() { return insurance; }
}
public class Insurance {
 private String name;
 public String getName() { return name; }
}

寻找一个人的车辆保险名字的查询如下:

String getCarInsuranceName(Optional person) {
 return person.flatMap(person -> person.getCar())
       .flatMap(car -> car.getInsurance())
       .map(insurance -> insurance.getName())
       .orElse(“Unknown”);
}

第一句:person.flatMap(person -> person.getCar()) 得到的是 Optional类型的car.

.flatMap(car -> car.getInsurance())得到的是Optional的Insurance;

.map(insurance -> insurance.getName())

文章目录
  1. 1. Optional