java8实战_2020-04-08 (Java8实战)第一部分

一、第一章

1、了解概念

a、Stream API

b、向方法传递代码的技巧。

c、接口中的默认方法。

1.1、

在java.util.stream中添加了一个Stream API;Stream就是一 系列T类型的项目。可以把它看成一种比较花哨的迭代器。Stream API的很多方法可以链 接起来形成一个复杂的流水线,推动这种做法的关键在于,现在你可以在一个更高的抽象层次上写Java 8程序了:思路变成 了把这样的流变成那样的流(就像写数据库查询语句时的那种思路),而不是一次只处理一个项 目。

另一个好处是,Java 8可以透明地把输入的不相关部分拿到几个CPU内核上去分别执行你的 Stream操作流水线——这是几乎免费的并行,用不着去费劲搞Thread了。

1.2、

并行与共享的可变数据

写代码时不能访问共享的可变数据。这些函数有时被称为“纯 函数”或“无副作用函数”或“无状态函数”,如果要写入的是一个共享变 量或对象,这就行不通了:如果两个进程需要同时修改这个共享变量怎么办? Java 8的流实现并行比Java现有的线程API更容易,因此,尽管可以使用synchronized来打 破“不能有共享的可变数据”这一规则,但这相当于是在和整个体系作对,因为它使所有围绕这 一规则做出的优化都失去意义了。在多个处理器内核之间使用synchronized,其代价往往比你 预期的要大得多,因为同步迫使代码按照顺序执行,而这与并行处理的宗旨相悖。 这两个要点(没有共享的可变数据,将方法和函数即代码传递给其他方法的能力)是我们平 常所说的函数式编程范式的基石。在命令式编程 范式中,写的程序则是一系列改变状态的指令。“不能有共享的可变数据”的要求意味着,一 个方法是可以通过它将参数值转换为结果的方式完全描述的;换句话说,它的行为就像一个数学 函数,没有可见的副作用。

1.3、方法作为值传递

Java 8的第一个新功能是方法引用。比方说,你想要筛选一个目录中的所有隐藏 文件。你需要编写一个方法,然后给它一个File,它就会告诉你文件是不是隐藏的。

幸好,File 类里面有一个叫作isHidden的方法。我们可以把它看作一个函数,接受一个File,返回一个布 尔值。但要用它做筛选,你需要把它包在一个FileFilter对象里,然后传递给File.listFiles 方法,如下所示:

File[] hiddenFiles = new File(".").listFiles(newFileFilter() {publicboolean accept(File file) {returnfile.isHidden();

}

});

如今在Java 8里,你可以把代码重写成这个样子:

File[] hiddenFiles = new File(".").listFiles(File::isHidden);

已经有了函数isHidden,因此只需用Java 8的方法引用::语法(即“把这 个方法作为值”)将其传给listFiles方法;开始用函数代表方法了。一个好处是,代码现在读起来更接近问题的陈述了。方法不再是二等值了。与用对象引用传递对象类似(对象引用是用new创建的),在Java 8里写下 File::isHidden的时候,就创建了一个方法引用,同样可以传递它。

955ac73a06c1417c003639d10fcb78b3.png

Lambda——匿名函数

除了允许(命名)函数成为一等值外,Java 8还体现了更广义的将函数作为值的思想,包括 Lambda(或匿名函数)。比如,可以写(int x) -> x + 1,表示“调用时给定参数x, 就返回x + 1值的函数”。可能会想这有什么必要呢?因为可以在MyMathsUtils类里面定义 一个add1方法,然后写MyMathsUtils::add1嘛!确实是可以,但要是没有方便的方法和类可用,新的Lambda语法更简洁。这句话的意思是“编写把函数作为一等值来传递的程序”。

1.4、默认方法

jdk1.8之后,接口可以写方法的实现,如List这个接口,实现了默认方法,需要用default关键字来声明

default void sort(Comparator super E>c) {

Object[] a= this.toArray();

Arrays.sort(a, (Comparator) c);

ListIterator i = this.listIterator();for(Object e : a) {

i.next();

i.set((E) e);

}

}

加入默认方法主要是为了支持库设计师,让他们能够写出更容易改进的接口,多用于程序的改进。

二、第二章

使用lambda表达式实现代码实例,跟以往用类、匿名内部类的对比。

public classFilteringApples {public static voidmain(String[] args) {

List inventory= Arrays.asList(new Apple(80,"green"),new Apple(155,"green"),new Apple(120,"red"));//[Apple{weight=80, color='green'}, Apple{weight=155, color='green'}]

List apples0 =FilteringApples.fileterApples(inventory, FilteringApples::isGreenApple);

System.out.println(apples0);//[Apple{weight=80, color='green'}, Apple{weight=155, color='green'}]

List apples1 = FilteringApples.fileterApples(inventory, (Apple a) -> "green".equals(a.getColor()));

System.out.println(apples1);//[Apple{weight=155, color='green'}, Apple{weight=120, color='red'}]

List apples2 =FilteringApples.fileterApples(inventory,

(Apple a)-> "red".equals(a.getColor()) || a.getWeight()>150);

System.out.println(apples2);

}public static List fileterApples(List inventory, Predicatep){

List result=new ArrayList<>();for(Apple apple:inventory){if(p.test(apple)){

result.add(apple);

}

}returnresult;

}public staticboolean isGreenApple(Apple apple){return "green".equals(apple.getColor());

}public static List filterGreenApples(Listinventory){

List result=new ArrayList<>();for(Apple apple:inventory){if("green".equals(apple.getColor())){

result.add(apple);

}

}returnresult;

}static classApple{private int weight=0;private String color="";

Apple(inti,String color){

weight=i;this.color=color;

}public intgetWeight() {returnweight;

}public void setWeight(intweight) {this.weight =weight;

}publicString getColor() {returncolor;

}public voidsetColor(String color) {this.color =color;

}

@OverridepublicString toString() {return "Apple{" +

"weight=" + weight +

", color='" + color + '\'' +

'}';

}

}

}

以前比较繁琐的方式:

public classFilteringApples {public static voidmain(String[] args) {

List inventory= Arrays.asList(new Apple(80,"green"),new Apple(155,"green"),new Apple(120,"red"));//[Apple{weight=80, color='green'}, Apple{weight=155, color='green'}]

List list1 = FilteringApples.filter(inventory, newColorApplePredicate());

System.out.println(list1);//[Apple{weight=155, color='green'}]

List list2 = FilteringApples.filter(inventory, newWeightApplePredicate());

System.out.println(list2);//[Apple{weight=155, color='green'}, Apple{weight=120, color='red'}]//使用匿名类

List list3 = FilteringApples.filter(inventory, newApplePredicate(){

@Overridepublicboolean check(Apple apple) {return apple.getWeight()>150 || "red".equals(apple.getColor());

}

});

System.out.println(list3);

}//体现了策略模式,想根据什么筛选就传递什么实现类

public static List filter(Listinventory ,ApplePredicate applePredicate){

List result=new ArrayList<>();for(Apple apple:inventory){if(applePredicate.check(apple)){

result.add(apple);

}

}returnresult;

}interfaceApplePredicate{

boolean check(Apple apple);

}static classColorApplePredicate implements ApplePredicate{

@Overridepublicboolean check(Apple apple) {return "green".equals(apple.getColor());

}

}static classWeightApplePredicate implements ApplePredicate{

@Overridepublicboolean check(Apple apple) {return apple.getWeight()>150;

}

}static classApple{private int weight=0;private String color="";

Apple(inti,String color){

weight=i;this.color=color;

}public intgetWeight() {returnweight;

}public void setWeight(intweight) {this.weight =weight;

}publicString getColor() {returncolor;

}public voidsetColor(String color) {this.color =color;

}

@OverridepublicString toString() {return "Apple{" +

"weight=" + weight +

", color='" + color + '\'' +

'}';

}

}

}

三、第三章

1、lambda表达式有三部分

a、参数列表——这里它采用了Comparator中compare方法的参数,两个Apple。

b、箭头——箭头->把参数列表与Lambda主体分隔开。

c、Lambda主体——比较两个Apple的重量。表达式就是Lambda的返回值了。

06dbd1fd34def5f7de7b22feb88fed54.png

2、Java8中有效的Lambda表达式

5c2f9eb73b4c878d993d0fdee70fb26a.png

3、测验,判断下面Lambda语法是否正确

根据上述语法规则,以下哪个不是有效的Lambda表达式?

(1) () -> {}

(2) () -> "Raoul"

(3) () -> {return "Mario";}

(4) (Integer i) -> return "Alan" + i;

(5) (String s) -> {"IronMan";}

答案:只有4和5是无效的Lambda。

(1) 这个Lambda没有参数,并返回void。它类似于主体为空的方法:public void run() {}。

(2) 这个Lambda没有参数,并返回String作为表达式。

(3) 这个Lambda没有参数,并返回String(利用显式返回语句)。

(4) return是一个控制流语句。要使此Lambda有效,需要使花括号,如下所示: (Integer i) -> {return "Alan" + i;}。 (5)“Iron Man”是一个表达式,不是一个语句。要使此Lambda有效,你可以去除花括号 和分号,如下所示:(String s) -> "Iron Man"。或者如果你喜欢,可以使用显式返回语 句,如下所示:(String s)->{return "IronMan";}。

4、Lambda示例

350893559af6cd9f4de54efa7e042f88.png

5、函数式接口

函数式接口就是只定义一个抽象方法的接口。哪怕接口有很多默认方法,只要接口只定义一个抽象方法,它仍然是一个函数式接口。现函数式接口带有@FunctionalInterface的标注。

作用:Lambda表达式允许直接以内联的形式为函数式接口的抽象方法提供实现,并把整个表达式作为函数式接口的实例,

具体说来,是函数式接口一个具体实现的实例。

用匿名内部类也可以完成同样的事情,只不过比较笨拙:需要提供一个实现,

然后再直接内联将它实例化。下面的代码是有效的,因为Runnable是一个只定义了一个抽象方法run的函数式接口:

1f38a78145659a908ce927155ccfe75d.png

6、jdk 中Predicate、Consumer、Function三个函数式接口

a、Predicate

java.util.function.Predicate接口定义了一个名叫test的抽象方法,它接受泛型 T对象,并返回一个boolean。在需要 表示一个涉及类型T的布尔表达式时,就可以使用这个接口。比如,你可以定义一个接受String 对象的Lambda表达式,如下所示。

@FunctionalInterfacepublic interface Predicate{

boolean test(T t);

}public static List filter(List list, Predicatep) {

List results = new ArrayList<>();for(T s: list){if(p.test(s)){

results.add(s);

}

}returnresults;

}

Predicate nonEmptyStringPredicate = (String s) -> !s.isEmpty();

List nonEmpty = filter(listOfStrings, nonEmptyStringPredicate);

b、Consumer

java.util.function.Consumer定义了一个名叫accept的抽象方法,它接受泛型T 的对象,没有返回(void)。需要访问类型T的对象,并对其执行某些操作,就可以使用 这个接口。

@FunctionalInterfacepublic interface Consumer{voidaccept(T t);

}public static void forEach(List list, Consumerc){for(T i: list){

c.accept(i);

}

}

forEach(Arrays.asList(1,2,3,4,5),

(Integer i)-> System.out.println(i)

);

c、Function

java.util.function.Function接口定义了一个叫作apply的方法,它接受一个 泛型T的对象,并返回一个泛型R的对象。如果需要定义一个Lambda,将输入对象的信息映射 到输出,就可以使用这个接口(比如提取苹果的重量,或把字符串映射为它的长度)。在下面的代码中,展示如何利用它来创建一个map方法,以将一个String列表映射到包含每个 String长度的Integer列表。

@FunctionalInterfacepublic interface Function{

R apply(T t);

}public static List map(Listlist,

Functionf) {

List result = new ArrayList<>();for(T s: list){

result.add(f.apply(s));

}returnresult;

}//[7, 2, 6]

List l =map(

Arrays.asList("lambdas","in","action"),

(String s)->s.length()

);

其他一些函数式接口:

c35f8602d7dc5287ec953f023dce8a0b.png

e85bf37eb88f63ec258f0479d513f6b7.png

984d4f18041f77323efae651fd284f36.png

最主要的一点是:如果需要什么样的类型,输出什么样的类型,都可以自己设计一个函数式接口来用。

7、方法引用

显式地指明方法的名称,代码的可读性会更好。它是如何工作的呢? 当你需要使用方法引用时,目标引用放在分隔符::前,方法的名称放在后面。

866fd3802cf6907a2708ac9dc6f7c247.png

方法引用主要有三类:

(1) 指向静态方法的方法引用(例如Integer的parseInt方法,写作Integer::parseInt)。

(2) 指 向 任意类型实例方法 的方法引用(例如 String 的 length 方法,写作 String::length)。

(3) 指向现有对象的实例方法的方法引用(假设你有一个局部变量expensiveTransaction 用于存放Transaction类型的对对象,它支持实例方法getValue,那么你就可以写expensiveTransaction::getValue)。

第二种和第三种方法引用可能乍看起来有点儿晕。

类似于String::length的第二种方法引 用的思想就是你在引用一个对象的方法,而这个对象本身是Lambda的一个参数。

例如,Lambda 表达式(String s) -> s.toUppeCase()可以写作String::toUpperCase。

但第三种方法引用 指的是,你在Lambda中调用一个已经存在的外部对象中的方法。例如,Lambda表达式 ()->expensiveTransaction.getValue()可以写作expensiveTransaction::getValue。

依照一些简单的方子,我们就可以将Lambda表达式重构为等价的方法引用,如图3-5所示。

194360db24f7902bbe7f10b08d34a200.png

看下面一个排序的例子,怎么一步步演化成简单的写法

//1

List inventory = new ArrayList<>();

inventory.addAll(Arrays.asList(new Apple(80,"green"), new Apple(155, "green"), new Apple(120, "red")));//[Apple{color='green', weight=80}, Apple{color='red', weight=120}, Apple{color='green', weight=155}]

inventory.sort(newAppleComparator());

System.out.println(inventory);//改一下第2位置的元素

inventory.set(1, new Apple(30, "green"));//2//[Apple{color='green', weight=30}, Apple{color='green', weight=80}, Apple{color='green', weight=155}]

inventory.sort(new Comparator() {public intcompare(Apple a1, Apple a2){returna1.getWeight().compareTo(a2.getWeight());

}});

System.out.println(inventory);//改一下第2位置的元素

inventory.set(1, new Apple(20, "red"));//3//[Apple{color='red', weight=20}, Apple{color='green', weight=30}, Apple{color='green', weight=155}]

inventory.sort((a1, a2) ->a1.getWeight().compareTo(a2.getWeight()));

System.out.println(inventory);//改一下第2位置的元素

inventory.set(1, new Apple(10, "red"));//4//[Apple{color='red', weight=10}, Apple{color='red', weight=20}, Apple{color='green', weight=155}]

inventory.sort(comparing(Apple::getWeight));

//inventory.sort(comparing(Apple::getWeight).reversed()); //逆序

System.out.println(inventory);

Comparator具有一个叫作comparing的静态辅助方法, 它可以接受一个Function来提取Comparable键值,并生成一个Comparator对象。

最终的代码读起来的意思就是:对库存进行排序,比较苹果的重量。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值