java8

Java8的新特性体验#

Lambda表达式和FunctionalInterface#

类似scala和python的lambda表达式终于面世,可以大大简化大量的匿名内部类代码,快速来看一眼。

概述@FunctionalInterface#

函数式接口是指一个接口中有且只有一个方法的接口,此时的接口可以加上一个@FunctionalInterface来标记这个interface是一个函数是接口。当然也不是强制的,只要一个接口中有且只有一个未实现的抽象方法,就可以当作是一个函数式接口。

  • FunctionalInterface可以用:lambda表达式,方法引用,或构造的引用来创建。

  • 加上@FunctionalInterface只是为了让编译器去检查我们的接口,如果不小心写了第二个抽象方法就会报错,编译不通过。(Object的方法除外,因为已经有默认实现)

  • 同时java8对interface有加强实现,如default的方法可以在子类中实现。

  • 同时java8还允许有static的实现。

  • 以上两个方式都不是抽象方法,不会影响我们的函数式接口的定义。

如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@FunctionalInterface
public interface InterfaceMethod {

/**
* lambda能用的接口都只能是一个抽象方法
* 注解不是必须的,如果一个接口符合"函数式接口"定义,那么加不加@FunctionalInterface 注解都没有影响。
* 但是如果接口超过一个抽象方法,加上它会报错,给我们一个提示。
*/
public void method1(); // 接口里的public可以省略,这里是写顺手了,下同。


//比如函数式注解@FunctionalInterface标记后,解开下面这一行就会编译报错
//public void method9(String param);

// 接口中static和default的实现不会影响函数式接口的实现,有任意多个也不影响

// Object的方法也例外,如下面也没关系
// public boolean equals();
}

使用的时候,假设用上面的方法来做回调:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

class BankService {

// 自定义回调的业务类,一般要求外面执行业务时传递一个符合接口规范的回调对象
public void doSth(String param, InterfaceMethod callback){
// 执行业务逻辑
System.out.println("业务方法开始回调。。。。");
callback.method1();
}
}


// java8之前的业务使用
// 创建业务对象,同时传递回调业务
BankService bank = new BankService();
// 执行业务,会自动回调
bank.doSth("取款",new InterfaceMethod() {
@Override
public void method1() {
// 回调的业务逻辑
System.out.println("执行完毕取款业务的callback逻辑....");
}
});

// java8之后的业务使用,lambda表达式就代表了我们bank这个方法所需的InterfaceMethod接口实现
bank.doSth("转账",()->{System.out.println("执行完毕转账业务的callback逻辑2....");};);

上面只是一个演示,有点丑陋。另外可以看到我们写了那么一个只有一个方法名的空的接口没啥意思。

如果我们业务有很多很多场景都要回调,没必要每个地方都定义一个空的接口,所以JDK给我们预制了常见的functionalInterface 我们可以看一眼常见的类型:

Lambda表达式#

Lambda表达式其实完成了实现接口并且实现接口里的方法这一功能.

  • 一个 Lambda 表达式可以有零个或多个参数
  • 参数的类型既可以明确声明,也可以根据上下文来推断。例如:(int a)与(a)效果相同
  • 所有参数需包含在圆括号内,参数之间用逗号相隔。例如:(a, b) 或 (int a, int b) 或 (String a, int b, float c)
    空圆括号代表参数集为空。例如:() -> 42
  • 当只有一个参数,且其类型可推导时,圆括号()可省略。例如:a -> return a*a
  • Lambda 表达式的主体可包含零条或多条语句
  • 如果 Lambda 表达式的主体只有一条语句,花括号{}可省略。匿名函数的返回类型与该主体表达式一致
  • 如果 Lambda 表达式的主体包含一条以上语句,则表达式必须包含在花括号{}中(形成代码块)。匿名函数的返回类型与代码块的返回类型一致,若没有返回则为空
1
2
3
4
5
6
() -> {return 0;},// 没有传入参数,有返回值
(int i) -> {return 0;},// 传入一个参数,有返回值
(int i) -> {System.out.println(i)},// 传入一个int类型的参数,但是没有返回值
(int i, int j) -> {System.out.println(i)},// 传入两个int类型的参数,但是没有返回值
(int i, int j) -> {return i+j;},// 传入两个int类型的参数,返回一个int值
(int i, int j) -> {return i>j;},// 传入两个int类型的参数,返回一个boolean值

如:

1
Runnable o1 = () -> { System.out.println("hi"); };

函数式接口在包jjava.util.function包中,主要包括:

Consumer#

顾名思义是给我们消费用的,看看他的参数和返回:

他是一个单个参数、无返回的一个函数式接口,用于我们消费参数并执行一个操作使用。

也就是: Consumer 一个参数,供消费用的。 void accept(T t);

1
2
3
4
5
6
7
8
9
10
// 单个参数、无返回的一个函数式接口
@FunctionalInterface
public interface Consumer<T> {

/**
* Performs this operation on the given argument.
* @param t the input argument
*/
void accept(T t);
}

使用方式如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 一个极简的演示
class BankService {

int balance = 0; // 余额

/**
* 充值并使用JDK自带的consumer函数接口进行回调
* @param amt 充值金额
* @param callback 回调余额
*/
public void recharge(int amt, Consumer<Integer>callback){
balance += amt;
callback.accept(balance);
}

}


// 上面自定义一个空的函数接口没啥意思,本身也是空的,所以JDK提供了很多默认的函数接口java.util.function里面
// 充值,并传入一个要求的consumer实现,来作为回调函数
bank.recharge(100,balance->System.out.println("recharge completed,余额是...."+balance));

当然那还有一堆定制更多的Consumer,如DoubleConsumer消费Double数据的,IntConsumer消费Int数据的,BiConsumer消费两个数据的。

更深入定制的如:ObjIntConsumer 是消费一个Obj,一个Int,他其实是一种BiConsumer,也就是消费两个对象。

1
2
3
4
5
6
7
8
9
10
11
@FunctionalInterface
public interface ObjIntConsumer<T> {

/**
* Performs this operation on the given arguments.
*
* @param t the first input argument
* @param value the second input argument
*/
void accept(T t, int value);
}

Function#

顾名思义,是一个操作。既然是一个操作就有参数和返回值。所以

1
2
3
4
5
6
7
8
9
10
11
12
13
// 一个参数,一个返回值
@FunctionalInterface
public interface Function<T, R> {

/**
* Applies this function to the given argument.
*
* @param t the function argument
* @return the function result
*/
R apply(T t);
}

应用如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class BankService {    
/**
* 充值并扣税
* 要求传入一个金额和一个扣税方法来计算:
* 传入int金额并返回int的税后金额
* @param amount
* @param calculateTax
*/
public void rechargeAndTax(int amount, Function<Integer, Integer>calculateTax){
// 对充值金额进行扣税
amount -= calculateTax.apply(amount);
this.balance += amount;
}
}


// 外面业务使用,充值100,并传入扣税的lambda,扣定额的10元税(只有一行,省略return,一个参数省略小括号)
bank.rechargeAndTax(100,amount->amount - 10);

Function的变种比较多如LongFunction就是传入一个long,然后进行计算返回一个自定义类型对象:

1
2
3
4
5
@FunctionalInterface
public interface LongFunction<R> {
R apply(long value);
}

类似的还有IntFunction是处理int类型入参的。DoubleFunction类似。

有个特殊点的,如需要两个参数,一个返回怎么办?答案是BiFunction,更多参数就没有了,要我们自己实现了。

1
2
3
4
5
6
7
8
9
10
11
12
@FunctionalInterface
public interface BiFunction<T, U, R> {

/**
* Applies this function to the given arguments.
*
* @param t the first function argument
* @param u the second function argument
* @return the function result
*/
R apply(T t, U u);
}

演示一个简单的双参数的Function使用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/**
* 消费,且消费前先校验
* 传入校验的方法
* @param amount
* @param validation
* @return
*/
public boolean cash(int amount, BiFunction<Integer,Integer,Boolean> validation){
if(validation.apply(amount,balance)){
balance -= amount;
System.out.println("取现"+amount+"元,余额:"+balance+"元。");
return true;
}else{
System.out.println("校验失败,余额不足!");
return false;
}
}




// 外面使用
// bank.cash(50, (amount,balance)->{return balance > amount;});
bank.cash(500, (amount,balance)->balance > amount);

Predicate#

可以看作是一个Function<T,Boolean>,也是进行一个计算,然后返回一个Boolean值而已

1
2
3
4
5
6
7
8
9
10
11
12
13
@FunctionalInterface
public interface Predicate<T> {

/**
* Evaluates this predicate on the given argument.
*
* @param t the input argument
* @return {@code true} if the input argument matches the predicate,
* otherwise {@code false}
*/
boolean test(T t);

}

Supplier#

没有参数,只是返回,一般用于工厂方法。

1
2
3
4
5
6
7
8
9
10
@FunctionalInterface
public interface Supplier<T> {

/**
* Gets a result.
*
* @return a result
*/
T get();
}

接口的static和default实现#

默认方法的作用:

Java8之前如果已经使用了一个接口,想给这个接口加一个方法,那么需要修改所有实现了这个接口的类,是非常恐怖的,而Java8可以允许我们给这个接口新增一个默认实现方法来解决(子类可以override)

当然如果最开始我们的是接口-抽象类-子类的方式的话,就不用这么麻烦,可以直接在抽象类中新增方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public interface commonInterface(){

public void method1();

/**
* java8的default实现
* 当需要新增一个所有实现类都需要的方法时,可以在接口中新增一个default的方法
* 而无需每个实现类都重新相同的实现一遍这个接口类
*
* 可以被其他的接口继承,可以被重写,刻意被继承的接口重新定义为一个普通的抽象方法
*
* 默认方法不算是抽象方法
*/
default public void method2(){
//do something
}

/**
* java8允许在接口中实现静态方法
* 静态方法不算是抽象方法
*/
public static void method4(){
//do something
}
}

方法引用::#

双冒号代表引用一个方法,通过方法引用来创建函数式接口的实现(引用一个方法来当作一个函数是方法的实现),如:

限制条件:

  • 方法实现只能调用一个方法,一行
  • 引用的那个方法和我们定义的函数式方法的参数和返回值是一致的

引用实例方法#

PrintStream类的println方法引用过来当作一个函数是接口:

1
2
3
4
5
6
// 假设我只是想打印一下,那么需要这么干
Consumer<String> consumer = s -> System.out.println(s);
consumer.accept("sam");
// 但是打印这个功能其实在System.out中已经实现了(其实是PrintStream类),就可以:
Consumer<String> consumer = System.out::println; // 将out里面的println方法视为一个Consumer<String> ,通过方法引用来创建函数式接口的实现
consumer.accept("sam");

因为 println的实现就是传入一个String,没有返回值,和我们Consumer<String> 的需求一模一样:

而且我们的需求是只用一行就能完成业务System.out.println(str),不能再有别的代码,否则就不行。

1
2
3
4
5
6
7
// println的实现就是传入一个String,没有返回值,和我们Consumer<String> 的需求一模一样
public void println(String x) {
synchronized (this) {
print(x);
newLine();
}
}

同理:

1
2
3
4
5
// 这里其实是使用了String.compareToIgnoreCase方法来当作一个Comparator<? super T> c的    int compare(T o1, T o2)实现
Arrays.sort(strArrs, (s1,s2)->s1.compareToIgnoreCase(s2));

// 类似的
Comparator<PersonBean> byName = Comparator.comparing(PersonBean::getUserName);

引用静态方法#

1
2
3
// 引用静态方法,三个int分别是求max的a,b和返回。这里引用public static int max(int a, int b) 实现
BiFunction<Integer,Integer,Integer> max = Math::max;
int bigger = max(2,3);

引用构造方法#

1
2
3
// 引用构造方法SrtingBuffer sb = new StringBuffer(1024);
Function<Integer,StringBuffer> sbCreator = StringBuffer::new;
sbCreator.apply(1024);

引用数组#

1
2
3
4
// 引用数组 Function<Integer,int[]> fun = n->new int[n];
Function<Integer,int[]> arrCreator = int[]::new;
arrCreator.apply(1024);

StreamAPI#

forEach是一个Consumer<T>#

1
studentList.forEach(each -> System.out.print(each.getUserName()));

map是一个Function<? super T, ? extends R>#

1
2
3
// 简略写法
studentList.stream().map(PersonBean::getAge).distinct().collect(Collectors.toList())
// studentList.stream().map(each->each.getAge()).distinct().collect(Collectors.toList())
  • 先使用map去处理每一个list的对象,里面使用person.getAge()返回了Int的年龄,然后使用distinct变成另一个stream,
  • 再collecte的时候引用了另外一个Collectors.toList()中实现好的方法

filter 过滤#

Stream<T> filter(Predicate<? super T> predicate);还是返回一个stream,后面继续操作

1
2
3
List<String>names = studentList.stream().filter(each->each.getAge()>16).sorted(Comparator.comparingInt(each->each.getAge())).map(PersonBean::getUserName).collect(Collectors.toList());

System.out.println("所有>18学生的姓名按照年龄排序:"+names);

sort#

Stream<T> sorted(Comparator<? super T> comparator);也返回一个stream,后面继续操作

看上面例子

reduce:#

求汇总

1
2
3
4
5
6
7
8
9
10
11
// 求大于16的人的年龄总和
public void testStream5() {
int sumAge = studentList.stream().map(each->{
if(each.getAge()>16){
return each.getAge();
}else{
return 0;
}
}).reduce(0,Integer::sum);// 初始值为0去累加
System.out.println("大于16的人的年龄总和:"+sumAge);
}

flatMap 展平#

1
2
3
4
5
6
7
8
public void testStream6() {
List<List<Integer>> list =Arrays.asList(Arrays.asList(1,2),Arrays.asList(3,4,5),Arrays.asList(6,7,8));
System.out.println("原始list的数据,是三个list:");
list.stream().forEach(System.out::println);
System.out.println("flatMap后,展平合并了:");
Stream<Integer> integerStream = list.stream().flatMap(Collection::stream);
integerStream.forEach(System.out::println);
}

输出:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
原始list的数据,是三个list:
[1, 2]
[3, 4, 5]
[6, 7, 8]
flatMap后,展平合并了:
1
2
3
4
5
6
7
8

Process finished with exit code 0

groupingBy#

1
2
3
4
5
// 按照班级分组
studentList.stream().collect(Collectors.groupingBy(PersonBean::getClassName)).forEach((k,v)->{
System.out.println("k="+k);
System.out.println("v="+v);
});

输出:

1
2
3
4
5
6
7
k=3班
v=[PersonBean(userName=王五, age=17, married=false, addr=null, isStudent=true, className=3班), PersonBean(userName=朱八, age=15, married=false, addr=null, isStudent=true, className=3班)]
k=2班
v=[PersonBean(userName=张三, age=16, married=false, addr=null, isStudent=true, className=2班), PersonBean(userName=李四, age=20, married=false, addr=null, isStudent=true, className=2班), PersonBean(userName=赵六, age=16, married=false, addr=null, isStudent=true, className=2班), PersonBean(userName=何七, age=16, married=false, addr=null, isStudent=true, className=2班)]

Process finished with exit code 0

skip、limit、count、distinct等等#

比较简单,略

Optional 作为一个if else的替代处理null#

先看一眼api

1
2
3
4
5
6
7
1、isPresent()        //有值则返回true
2、get(): //值存在时返回值,否则抛出一个NoSuchElement异常(所以调这个,一般先判断上面方法返回是否为true)
3、orElse(T other) //值存在时返回值,否则返回一个默认值
4、ifPresent(Consumer<T> block) //会在值存在的时候执行给定的代码块
5、orElseThrow(Supplier<? extends X> exceptionSupplier) //与get()类似,不同的是可以自定义异常类型
6、orElseGet(Supplier<? extends T> other) //orElse方法的延迟调用版,Supplier方法只有在Optional对象不含值时才执行调用
7、map/flatMap/filter //与Stream中用法类似

再看看源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
/**
* final修饰代表不能被子类继承
*/
public final class Optional<T> {
/**
* 创建一个空容器
*/
private static final java.util.Optional<?> EMPTY = new java.util.Optional<>();

/**
* 传入的值
*/
private final T value;

/**
* 构造函数私有化 说明不能被外部new
*/
private Optional() {
this.value = null;
}

/**
* 私有化构造函数
*/
private Optional(T value) {
this.value = Objects.requireNonNull(value);
}

/**
* 获取空容器
*/
public static <T> java.util.Optional<T> empty() {
@SuppressWarnings("unchecked")
java.util.Optional<T> t = (java.util.Optional<T>) EMPTY;
return t;
}


/**
* 传入的对象不能为空 否则抛异常
*/
public static <T> java.util.Optional<T> of(T value) {
return new java.util.Optional<>(value);
}

/**
* 传入的对象可以为空
*/
public static <T> java.util.Optional<T> ofNullable(T value) {
return value == null ? empty() : of(value);
}

/**
* 获取容器对象的方法 注意 如果用这个方法则代表容器中一定有对象,否则抛异常
*/
public T get() {
if (value == null) {
throw new NoSuchElementException("No value present");
}
return value;
}

/**
* 判断容器对象是否为空
*/
public boolean isPresent() {
return value != null;
}

/**
* 如果容器对象为空 则返回当前对象
*/
public T orElse(T other) {
return value != null ? value : other;
}

/**
* 传入Consumer编程式接口参数
*/
public void ifPresent(Consumer<? super T> consumer) {
if (value != null)
consumer.accept(value);
}

/**
* 传入Predicate编程式接口参数
*/
public java.util.Optional<T> filter(Predicate<? super T> predicate) {
Objects.requireNonNull(predicate);
if (!isPresent())
return this;
else
return predicate.test(value) ? this : empty();
}

/**
* 传入Function编程式接口参数
*/
public <U> java.util.Optional<U> map(Function<? super T, ? extends U> mapper) {
Objects.requireNonNull(mapper);
if (!isPresent())
return empty();
else {
return java.util.Optional.ofNullable(mapper.apply(value));
}
}

/**
* 传入Function编程式接口参数
*/
public <U> java.util.Optional<U> flatMap(Function<? super T, java.util.Optional<U>> mapper) {
Objects.requireNonNull(mapper);
if (!isPresent())
return empty();
else {
return Objects.requireNonNull(mapper.apply(value));
}
}

/**
* 传入Supplier编程式接口参数
*/
public T orElseGet(Supplier<? extends T> other) {
return value != null ? value : other.get();
}

/**
* 传入Supplier编程式接口参数
*/
public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X {
if (value != null) {
return value;
} else {
throw exceptionSupplier.get();
}
}
}

最后试试:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
    public static void func1(String param){
// 传了就有,没传报空指针
// Optional<String> optional = Optional.of(param); // 不可为空,否则此行报错
// System.out.println(optional.get());

Optional<String> optional1 = Optional.ofNullable(param);
System.out.println(optional1.orElse("如果是null降级为此"));
// 上面相当于传统的写法:
// System.out.println(param == null? "如果是null降级为此" : param);

Optional<String> optional3 = Optional.ofNullable(param);
// 也可以在为空的时候使用一个Supplier进行降级,里面使用稍微复杂一点的逻辑进行降级
System.out.println(optional3.orElseGet(()->{return "123456".substring(1);}));

// Optional<String> optional2 = Optional.ofNullable(param);
// optional2.get(); 为空的时候也会报错 NoSuchElementException: No value present
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public static void func2(PersonBean person){
// 判断传入的user是否为null,是的话引用构造方法新建一个
Optional<PersonBean> userOp = Optional.ofNullable(person);
PersonBean personBean = userOp.orElseGet(PersonBean::new);

Optional<Integer> ageOp = Optional.ofNullable(personBean.getAge());
if(ageOp.isPresent()){
// op2.map((age)->String.valueOf(age));
String ageStr = ageOp.map(String::valueOf).get();
// ...
}else{
// ...
}
}

Comparable和Comparator#

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
/**
* comparable接口需要对象去实现,相当于表明“我是可以支持排序的”
* 因为实现了comparable接口的compareTo方法,可以直接排序。
* 实现Comparable接口这种方法有侵入性,而且已有的类很难去修改代码实现接口。
*/
public static void testComparable(){
List<Car> carList = getCarList();
Collections.sort(carList); // Comparable的Car的List,所以可以直接排序
carList.forEach(System.out::print);
}





@Data
@AllArgsConstructor
class Car implements Comparable{ // 表明是支持直接排序的
private String name;
private double price;

@Override
public int compareTo(Object o) {
Car another = (Car)o;
return this.price - another.getPrice()>0?1:-1;
}
}

Comparator 比较器,无侵入性,支持自定义#

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
List<User> users = getUserList();
// 要排序我们一般使用Collections.sort(List<T> list, Comparator<? super T> c) {
// 需要实现一个List泛型所需的比较器
// 1.排序,最原始的写法,我们写一个匿名内部类Comparator,来定义比较的规则
Comparator userComparator = new Comparator<User>() {
@Override
public int compare(User o1, User o2) {
return o1.getAge()-o2.getAge(); // 按照年龄从小到大排序
}
};
// 然后使用这个比较器
Collections.sort(users, userComparator);
users.forEach(System.out::print);

// 2.上面的Comparator是一个函数式接口,所以我们可以借助java8进行优化,一行搞定
users = getUserList();
// Comparator函数式接口只有一个方法compare,传入两个泛型参数,输出一个比较结果int,所以:
Collections.sort(users, (o1,o2)->o1.getAge()-o2.getAge());
users.forEach(System.out::print);

// 3.进一步,JDK为我们内置了常见的比较器,Comparator.comparingInt就会返回我们上面所需的函数式接口Comparator
users = getUserList();
Collections.sort(users, Comparator.comparingInt(User::getAge));
users.forEach(System.out::print);






@Data
@AllArgsConstructor
class User{
private int age;
private String name;
}

optional 参考了 https://www.cnblogs.com/qdhxhz/p/12056745.html