Java8初探
一、前言之一个挑苹果的故事
有一天,妈妈给了你100元钱,想让你去买一些苹果,可是市面上苹果种类、大小繁多。但是,人作为高度智能的综合体,我们通过视觉、触觉等收集外界信息,经大脑信息处理后就能够轻松地完成这个任务。计算机则不然,计算机在日常生活中我们觉得它无所不能,它其实是很笨的,需要我们一步步告诉它怎么做。下面我们以计算机为背景,使用Java语言,考虑这个挑苹果的需求。
二、集合操作的场景迭代
2.1 苹果类的定义与初级解决方案
- 我们假设苹果有两个属性,一个是颜色,一个是重量(单位:g),我们可以很快地写出以下代码。
package pojo;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Predicate;
/**
* Created with IntelliJ IDEA.
*
* @Author: tanqw
* @Date: 2024/01/12/18:13
* @Description:
*/
public class Apple {
private int weight;
private String color;
public int getWeight() {
return weight;
}
public void setWeight(int weight) {
this.weight = weight;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public Apple(int weight, String color) {
this.weight = weight;
this.color = color;
}
@Override
public String toString() {
return "Apple{" +
"weight=" + weight +
", color='" + color + '\'' +
'}';
}
}
-
需求1:现在,我们想要挑选一些青苹果。我们会想到在Apple类中添加一个方法,给定一个List返回一个全为青苹果的集合。
下面是Apple类中的挑选青苹果的方法:
/**
*
* @param source
* @return Java8之前的做法,筛选青苹果
*/
public static List<Apple> filterGreenApple(List<Apple> source){
List<Apple> apples = new ArrayList<>();
for (Apple apple : source) {
if("green".equals(apple.getColor())){
apples.add(apple);
}
}
return apples;
}
我们在测试中使用该方法,可以写出一下代码:
package service;
import pojo.Apple;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
/**
* Created with IntelliJ IDEA.
*
* @Author: tanqw
* @Date: 2024/01/13/9:40
* @Description:
*/
public class Test20240113 {
public static void main(String[] args) {
ArrayList<Apple> apples = new ArrayList<>();
Apple apple = new Apple(120,"green");
Apple apple1 = new Apple(130,"red");
Apple apple2 = new Apple(100,"yellow");
apples.add(apple);
apples.add(apple1);
apples.add(apple2);
//1.需求1:想要筛选出青色的苹果
List<Apple> greenAppleList = Apple.filterGreenApple(apples);
System.out.println(greenAppleList);
System.out.println("--------------------------------");
}
运行结果:
[Apple{weight=120, color='green'}]
- 需求2:现在,我们的需求变了,我们想要挑选一些大一点的苹果。我们会想到在Apple类中添加一个方法,给定一个List返回一个全为大苹果的集合,这里假定大于等于120g的苹果为大苹果。我们会想到,在Apple类中添加一个和前面类似的方法,来判断,方法如下:
/**
*
* @param source
* @return Java8之前的做法,筛选重量大于等于120g苹果
*/
public static List<Apple> filterBigApple(List<Apple> source){
List<Apple> apples = new ArrayList<>();
for (Apple apple : source) {
if(apple.getWeight()>=120){
apples.add(apple);
}
}
return apples;
}
测试主类中添加以下代码,发现可以完成这个需求。
System.out.println("--------------------------------");
//2. 需求2:想要筛选出重量>=120g的苹果
List<Apple> bigApple = Apple.filterBigApple(apples);
System.out.println(bigApple);
运行结果:
--------------------------------
[Apple{weight=120, color='green'}, Apple{weight=130, color='red'}]
- 我们虽然完成了以上两个需求,但是我们引起了复制粘贴代码在软件工程中的问题,就是后续有代码修改时,我们可能会修改了其中一个方法,而忘记了另一个方法。而且,这两个方法只在if语句中有一行不一样,造成了代码的冗余,这种做法是不推荐的。
2.2 方法引用与第二代解决方案
我们可以对以上挑选青苹果和大苹果的方法进行分析,对它们进行一个抽提,写出通用性更强的代码,if语句里面需要的是一个布尔条件,我们可以想到在Apple类中增加以下方法:
public static boolean isGreenApple(Apple apple){
return "green".equals(apple.getColor());
}
public static boolean isBigApple(Apple apple){
return apple.getWeight()>=120;
}
/**
*
* @param source
* @param predicate
* @return Java8思想,将代码作为参数传递,减少了代码冗余与软件工程问题。
*/
public static List<Apple> filterApple(List<Apple> source, Predicate<Apple> predicate){
List<Apple> apples = new ArrayList<>();
for (Apple apple : source) {
if(predicate.test(apple)){
apples.add(apple);
}
}
return apples;
}
然后在测试主类中使用,其中Apple::isGreenApple称之为方法引用,即“这个方法作为值来传递”,所谓的代码参数化,如下所示:
System.out.println("---------------------------------");
//新的方法完成需求1和2
List<Apple> appleList = Apple.filterApple(apples, Apple::isGreenApple);
System.out.println(appleList);
System.out.println("---------------------------------");
List<Apple> appleList1 = Apple.filterApple(apples, Apple::isBigApple);
System.out.println(appleList1);
运行结果如下所示:
---------------------------------
[Apple{weight=120, color='green'}]
---------------------------------
[Apple{weight=120, color='green'}, Apple{weight=130, color='red'}]
2.3 lambda表达式与第三代解决方案
前面的将代码作为值传递的方法引用虽然好用,但是每增加一种挑选方法,就需要在Apple类中写一个短小的静态方法,是开发者不喜欢的,lambda表达式应运而生。可以让代码更简洁、清晰。例如,挑选大的青苹果不用在Apple类写一个方法,直接使用lambda表达式,代码如下所示:
System.out.println("---------------------------------");
//3. 需求3:想要筛选出绿色的,且重量>=120g的苹果。使用lambda表达式完成这个需求。
List<Apple> appleList2 = Apple.filterApple(apples, (Apple a) -> ("green".equals(a.getColor()) && a.getWeight() >= 120));
System.out.println(appleList2);
运行结果如下所示:
---------------------------------
[Apple{weight=120, color='green'}]
2.4 集合流与第四代解决方案
学过Java8集合流操作的同学们都知道,要完成挑苹果这个需求,根本不需要在Apple类中添加任何方法,直接使用集合流中的方法就能完成,代码如下所示:
System.out.println("---------------------------------");
//进阶方法,集合流处理,不需要方法定义,直接使用。
List<Apple> collect = apples.stream().filter(a -> a.getWeight() >= 120 && "green".equals(a.getColor())).collect(Collectors.toList());
System.out.println(collect);
运行结果如下所示:
---------------------------------
[Apple{weight=120, color='green'}]
三、好好总结一下吧
- Java8新特性有几个重要概念,就是方法引用、代码行为参数化、lambda表达式、集合流。
- Java8新特性简化了我们写代码的过程,让我们的代码更加简洁,更加通用。
运行结果如下所示:
---------------------------------
[Apple{weight=120, color='green'}]