1. Lambda 入门
1.1 lambda表达式简介
- Lambda表达式是一个匿名函数, 我们可以把Lambda表 达式理解为是一段可以传递的代码(将代码像数据一样 进行传递) 。可以写出更简洁、更灵活的代码。作为一
种更紧凑的代码风格,使得Java语言表达能力得到了提升。JDK8 中引入了一个新的操作符" -> "该操作符称为箭头操作符或Lambda操作符,箭头操作符将Lambda表
达式拆分为两部分: - 左侧:Lambda表达式的参数列表。对应接口中抽象方法的参数列表。
- 右侧:Lambda表达式中所需要执行的功能,即Lambda
体。对应接口中抽象方法的实现
1.2 lambda基本使用
- 接口
public interface Bird {
void fly(Integer times);
}
- 三种实现方法
- 注意 : Lambda表达式需要函数式接口的支持, 函数式接口就是: 接口中有且仅有一个抽象方法的接口
package cn.knightzz.work;
import cn.knightzz.service.Bird;
public class LambdaTest {
public static void main(String[] args) {
Bird bird01 = new BirdImpl();
bird01.fly(1000);
// 实现接口的方式二: 匿名内部类
Bird bird02 = new Bird() {
@Override
public void fly(Integer times) {
System.out.println("方式二 : 小鸟飞行了 : " + times + "s");
}
};
bird02.fly(1000);
// 方式三: lambda表达式 : (参数1,参数2,...) -> {}
// TODO:Lambda表达式需要函数式接口的支持, 函数式接口就是: 接口中有且仅有一个抽象方法的接口
Bird bird03 = (param) -> {
System.out.println("方式三 : 小鸟飞行了 : " + param + "s");
};
bird03.fly(1000);
}
}
/**
* 实现接口的方式一
*/
class BirdImpl implements Bird {
/**
* 飞行
*
* @param times 飞行时间
*/
@Override
public void fly(Integer times) {
System.out.println("方式一: 小鸟飞行了 : " + times + "s");
}
}
2. Lambda 基本语法
-
我们可以使用 lambda 语法代替匿名方法的创建, Lambda语法如下
-
如果 lambda 方法体中只有一行代码, {} 可以省略
(参数1,参数2,...) -> {}
-
注意:Lambda表达式需要函数式接口的支持
2.1 函数式接口
- 接口中有且仅有一个抽象方法(必须被重写的方法)的接口,称为函数式接口。 注意是必须被重写的方法!!!, 默认方法不是必须被重写的,但是我们也可以重写它!
default
修饰方法只能在接口中使用(只能被实现了这个接口的对象调用),在接口种被default
标记的方
法为普通方法,可以直接写方法体。- 可以使用注解
@FunctionalInterface
修饰,可以检查是否是函数式接口。如果不是函数型接口会报错
@FunctionalInterface
public interface Dog {
/**
* 狗叫
*
* @param dogName 狗的名字
*/
void yap(String dogName);
// void flaw(); // 非函数式接口会报错
/**
* 默认方法, 实现 Dog 接口, 不是必须需要实现该方法
*/
default void fly() {
System.out.println("默认方法 fly() ");
}
/**
* 默认方法,可以定义多个
*/
default void flaw() {
System.out.println("默认方法 flaw() ");
}
}
- Lambda表达式需要函数式接口的支持以前我们整个 java 的继承关系已经使用了很多年,如果在 java 新版本中为了迎合函数式接口一个接口中只有一个抽象方法,会导致以前很多接口失效,那么别人也没法进行 jdk 的升级所以提供了默认方法,在 jdk1.8 之后,可以在接口中定义默认方法.
- 注意函数式接口中只能有一个是必须被重写的方法!!!, 默认方法不是必须被重写的,但是我们也可以重写它!
- 所以, 我们可以给其他接口上添加 default , lambda只需要实现函数式接口
class BigDog implements Dog {
/**
* 函数式接口
*
* @param dogName 狗的名字
*/
@Override
public void yap(String dogName) {
System.out.println("BigDog name is : " + dogName);
}
// 实现的都是默认接口
@Override
public void flaw() {
System.out.println("一只bigDog在抓");
}
// 实现的都是默认接口
@Override
public void fly() {
System.out.println("一只bigDog在飞");
}
}
- 一些固定的接口我们可以设置为默认接口并实现他, 需要根据具体业务变化的接口我们可以定义成函数式接口, 动态的实现具体的逻辑
public class FunctionInterfaceTest {
public static void main(String[] args) {
Dog dog = (name) -> {
System.out.println("dog name is : " + name);
};
dog.yap("cat");
dog.flaw();
dog.fly();
// 这个方法可以根据实际业务需求来重写
Dog dog02 = (name) -> {
System.out.println("dog name is : " + name);
};
dog02.yap("cat");
BigDog bigDog = new BigDog();
bigDog.flaw();
bigDog.fly();
bigDog.yap("pig");
}
}
3. 内置函数式接口
Lambda的实现依赖于函数式接口,接口可以我们自己定义,当然也可以使用JDK已经提供好的一些函数式
接口,这些接口基本已经能够满足我们的常用操作,并且在集合等框架中已经广泛地使用了,所以我们可以直接使用这些接口。
- 消费型、供给型、函数型、断定型
- 消费型接口 Comsumer void accept(T t)
- 供给型接口 Supplier T get()
- 函数型接口 Function<T,R> R apply(T t)
- 断定型接口 Predicate boolean test(T t)
- 消费性:只进不出
供给型:白手起家,空手套白狼
函数型:礼尚往来
断定行:鉴定评审 - 注意:关注的只有接口中方法的参数和返回值即可
3.1 Consumer 消费型接口
带一个参数,在方法体中使用完了就完了,例如在Collection中的 forEach 方法则需要一个Consumer接口的实现类对象。
public class CollectionInterface {
public static void main(String[] args) {
// 消费型接口
Collection<String> list = new ArrayList<>();
list.add("aaa");
list.add("aaa2");
list.add("aaa3");
list.add("aaa4");
// forEach(Consumer<? super T> action)
list.forEach(item -> System.out.println(item));
}
}
3.2 Supplier 供给型接口
public class SupplierInterface {
public static void main(String[] args) {
SupplierInterface supplierInterface = new SupplierInterface();
List<Integer> numberList = supplierInterface.getNumberList(10, () -> (int) (Math.random() * 100));
numberList.forEach(item -> System.out.println(item));
}
public List<Integer> getNumberList(int num, Supplier<Integer> data) {
List<Integer> list = new ArrayList<>();
for (int i = 0; i < num; i++) {
Integer n = data.get();
list.add(n);
}
return list;
}
}
3.3 Function <T, R> 函数型接口
-
函数通过 apply(str) 调用我们自己编写的业务逻辑
-
自定义 apply
String result = functionalInterface.strHandler("abc", (str) -> { // 注意: 这里的 str 就是 function.apply(str) 的 str return str.toUpperCase(); });
-
核心代码
package cn.knightzz.inter;
import java.util.function.Function;
/**
* @author knightzz98
* @title: FunctionInterface
* @projectName JavaBase
* @description:
* @date 2021/9/4 18:29
*/
public class FunctionInterface {
public static void main(String[] args) {
FunctionInterface functionalInterface = new FunctionInterface();
String result = functionalInterface.strHandler("abc", (str) -> {
// 注意: 这里的 str 就是 function.apply(str) 的 str
return str.toUpperCase();
});
System.out.println("result = " + result);
}
/**
* 处理字符串
* Function<String, String> : 第一个是 apply(param) 参数值类型, 第二个是返回值类型
*
* @return 处理完成的字符串
*/
public String strHandler(String str, Function<String, String> function) {
// 这里可以写统一处理逻辑
return function.apply(str);
}
}
3.4 Predicate 断言型接口
-
predicate.test(data) 返回值为 boolean 类型
-
我们自己编写的代码就相当于自定义 test(data) 方法
List<String> strs = pre.filterStr(Arrays.asList(datas), (data) -> { // 编写 predicate.test(data) 方法的逻辑, 返回值为 boolean return data.length() > 2; });
-
核心代码
package cn.knightzz.inter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.function.Predicate;
/**
* @author knightzz98
* @title: PredicateInterface
* @projectName JavaBase
* @description: 断言型接口
* @date 2021/9/4 18:37
*/
public class PredicateInterface {
public static void main(String[] args) {
PredicateInterface pre = new PredicateInterface();
String[] datas = new String[]{"aaaa", "bb", "ccc"};
List<String> strs = pre.filterStr(Arrays.asList(datas), (data) -> {
// 编写 predicate.test(data) 方法的逻辑, 返回值为 boolean
return data.length() > 2;
});
strs.forEach(item -> System.out.println(item));
}
public List<String> filterStr(List<String> datas, Predicate<String> predicate) {
List<String> list = new ArrayList<>();
for (String data : datas) {
if (predicate.test(data)) {
list.add(data);
}
}
return list;
}
}
4. 方法引用
简单看了下, 性能没提升, 代码可读性变差, 除了代码短了, 没别的用处, 预测用了肯定会挨骂, 暂时定义一个目录, 用到了再学