函数式接口与 Lambda 表达式
1.函数式接口
举例复习接口的匿名实现
- 假设有接口 InterfaceA , 该接口中有抽象方法 T methodA(T t)
- 在一般情况下如果想要调用这个方法,需要创建一个类,这个类继承InterfaceA这个接口,并重写抽象方法,根据需求对方法进行具体的实现
- 假设现在有两个需求:
1) 现有List 集合,去除不包含’a’的数据并返回
2) 现有List 集合,去除不包含小于10的数据并返回
3) 在不使用接口匿名实现的情况下需要继承这个接口并重写两次抽象方法,进行具体实现,如果还需要去判断List,List ? - 直接创建接口的对象,在大括号中重写抽象方法进行具体的实现
//接口的匿名实现类,在Runnable接口中有一个抽象方法run();
//假设此时需要调用该方法,但是不想创建该接口的实现类,并且可以根据需求进行不同的实现
//此时就可以使用匿名实现类,创建接口对象,在new 类名(){};
//大括号中对该接口的方法进行重写
Runnable r1 = new Runnable(){
@Override
public void run(){
System.out.println("接口匿名实现类,重写抽象方法执行...");
}
};
r1.run();
函数式接口
- 什么是函数式接口: 只包含一个抽象方法的接口称为函数式接口,可以通过@FunctionalInterface 来检查创建的接口是否是函数式接口
- java8 在 java.util.function包下提供了预定义的函数式接口: 分为消费型, 供给型, 函数型, 断定型:
2. Lambda表达式
什么是Lambda:
- 可以理解为函数式接口的实例化,在通过Lambda不创建类的情况下匿名实现只有一个抽象方法的接口简化写法
- 示例:
//接口的匿名实现:Comparator中只有一个抽象方法,是java8提供的内置供给型函数式接口
//new创建对象时,在大括号中重写compare方法,根据需求进行实现
Comparator<Integer> com1 = new Comparator<Integer>(){
@Override
public int compare(Integer o1, Integer o2){
return Integer.compare(01, 02);
}
};
//通过接口对象调用重写的抽象方法
int i1 = com1.compare(2,3);
System.out.println(i1);
//通过 Lambda 实现
//Lambda 写法解释: "->" 为Lambda箭头符
//"->"左边的括号 为抽象方法形参列表
//"->"右边的 "{};" 为抽象方法实现的方法体
Comparator<Integer> com2 = (o1, o2) -> Integer.compare(o1, o2);
int i2 = com2.compare(2,3);
System.out.println(i2);
//Lambda 方法引用模式
Comparator<Integer> com3 = Integer :: compare;
int i3 = com3.compare(2,3);
System.out.println(i3);
Lambda的几种编写规则解释示例
@Test
public void test1(){
//1.没有形参没有返回值
//接口匿名实现
Runnable r1 = new Runnable(){
//重写的Runnable接口中 run()抽象方法
@Override
public void run(){
System.out.println("接口匿名实现类,重写抽象方法执行...");
}
};
//调用
r1.run();
//Lambda编写
Runnable r2 = () -> {
System.out.println("Lambda接口匿名实现类,重写抽象方法执行...");
};
//2.需要形参,直接将方法的形参放在小括号中
//接口的匿名实现
Consumer<String> con = new Consumer<String>(){
@Override
public void accept(String s){
System.out.println(s);
}
};
//Lambda编写
Consumer<String> con2 =(String s) -> {
System.out.println(s);
};
//3.可以根据数据类型推断出形参类型的,形参类型也可以省略
//解释: 例如 Consumer 接口中的 accept()抽象方法中需要一个String类型的形参
//注意,这个形参在定义接口与方法时,由接口到方法,是通过泛型来指定的
//也就是创建这个接口的对象使用什么泛型类型,那么抽象方法的实现也是什么类型
//根据接口对象就可以推断出来,叫做类型推断,所以可以省略
Consumer<String> con3 = new Consumer<String>(){
@Override
public void accept(String s){
System.out.println(s);
}
};
//Lambda编写
Consumer<String> con4 = (s) -> {
System.out.println(s);
};
//4.由3中只有一个参数,并且参数类型可以推断出来,在编写lambda时,小括号也可以省略
Consumer<String> con5 = s -> {
System.out.println(s);
};
//5.当 Lambda 执行体只有一条执行语句时 "->" 的大括号也可以省略
Consumer<String> con6 = s -> System.out.println(s);
//6.当 Lambda 执行体有 return 返回数据时
//并且只有一条执行语句,想要省略大括号,"return" 关键字也要省略掉
//接口的匿名实现
Comparator<Integer> com1 = new Comparator<Integer>(){
@Override
public int compare(Integer o1, Integer o2){
return Integer.compare(01, 02);
}
};
//Lambda编写
Comparator<Integer> com2 = (o1, o2) -> Integer.compare(o1, o2);
}
Lambda 的方法引用与构造器引用
方法引用的解释与编写:
- 当要传递给Lambda的方法体是一个已经实现的方法了,就可以使用方法引用,使用 " 类或对象 :: 属于该类或该对象的方法" 引用方法
- 方法引用的引用方式:
对象 :: 实例方法名 (该方法的形参列表与返回值类型必须与抽象方法保持一致)
类 :: 静态方法名 (该方法的形参列表与返回值类型必须与抽象方法保持一致)
类 :: 实例方法名 (特殊情况,类是方法体中用来调用另外一个实例方法的数据对象的类型)
方法引用示例:
public class ArrayTest {
@Test
public void testName2() {
//方法引用
//在ArrayTest类中存在existMethod()非静态方法,与existMethodReturn()静态方法
//通过创建ArrayTest类对象,根据方法的形参列表,与返回值的类型对应不同的函数式接口,引用这两两个方法,
ArrayTest objAt = new ArrayTest();
//1"对象 :: 非静态方法名" 引用示例
//普通方式
Consumer<String> con1 = strVal ->System.out.println("普通Lambda:"+strVal);
//方法引用
//Consumer 消费型函数式接口,接口中的accept()方法与
//ArrayTest中existMethod()非静态方法的形参列表,返回值类型一致
//方法体也符合实际需求,所以创建ArrayTest对象,使用"::"进行引用existMethod方法
Consumer<String> con2 = objAt :: existMethod;
con2.accept("aaa");
//2"类 :: 静态方法名" 引用示例
//普通方式
Supplier<String> supplier1 = () -> ArrayTest.existMethodReturn();
//Supplier供给类型函数式接口,接口中的get()方法与
//ArrayTest中existMethodReturn()静态方法的形参列表,
//返回值类型一致,方法体也符合实际需求.使用ArrayTest类,进行引用
Supplier<String> supplier2 = ArrayTest :: existMethodReturn;
String s = supplier2.get();
//两个参数的方法引用示例:
//此处Comparator的方法体是通过Integer调用compare()方法,比对t1与 t2两个参数的大小
Comparator<Integer> com1 = (t1, t2) -> Integer.compare(t1, t2);
//方法引用
Comparator<Integer> com2 = Integer :: compare;
int i = com2.compare(2,3);
//3"类 :: 实例方法",注意前面的类是谁的类,示例1
//分析: 此处通过Lambda实现的Comparator中compare抽象方法
//该方法传递了两个参数 s1,s2,注意方法体是s1去调用String的
//compareTo方法去使用的
Comparator<String> com3 = (s1, s2) -> s1.compareTo(s2);
//在使用"类 :: 实例方法名" 类是方法体中调用方法的对象类型也就是s1的类型
//注意此时引用的方法与Comparator接口中的compare()方法的形参列表并不一致
Comparator<String> com4 = String :: compareTo;
//分析实际执行: 通过前面的"abc"调用compareTo("abc")方法
com4.compare("abc","abd");
//"类 :: 实例方法名" 示例2
BiPredicate<String, String> pre1 = (s1, s2) -> s1.equals(s2);
//分析方法体,通过s1调用equals()方法得出:调用equals的类型为String类型
//调用的是equals()方法
BiPredicate<String, String> pre2 = String :: equals;
Boolean b = pre2.test("aa","cc");
//"类 :: 实例方法名" 示例3
Function<ArrayTest, String> fun1 = arrayTest -> arrayTest.get();
//分析方法体,传递的一个参数是ArrayTest类型,返回String类型,并且传递
//的参数在方法体中是用来调用该类参数的实例方法get()来使用的,得到
Function<ArrayTest, String> fun2 = ArrayTest :: get;
fun2.apply(objAt);
}
public void existMethod(String str){
System.out.println("已经存在的方法:"+str);
}
public static String existMethodReturn(){
return "已存在静态方法返回参数";
}
public String get(){
return "已存在实例方法返回参数";
}
}
构造器引用示例
public class ArrayTest {
private String name;
private Integer age;
public ArrayTest(){
}
public ArrayTest(String name){
this.name = name;
}
public ArrayTest(String name, Integer age){
this.name = name;
this.age = age;
}
@Test
public void test4(){
//构造器引用
//1通过供给类型接口Supplier,该接口的抽象方法<T> get() 返回一个ArrayTest对象
Supplier<ArrayTest> supplier1 = () -> new ArrayTest();
//通过new引用该类的构造器
Supplier<ArrayTest> supplier2 = ArrayTest :: new;
ArrayTest sArrayTest = supplier2.get();
//2通过函数类型接口Function中的 R apply(T t) 返回一个ArrayTest对象
Function<String, ArrayTest> fun1 = str -> new ArrayTest("小明");
//通过new引用该类的构造器,自动寻找带一个参数的构造
Function<String, ArrayTest> fun2 = ArrayTest :: new;
ArrayTest fArrayTest = fun2.apply("小黑");
//3通过BiFunction中的 R apply(T t, U u) 返回一个ArrayTest对象
BiFunction<String, Integer, ArrayTest> bif1 = (name, age) -> new ArrayTest("小红", 13);
//通过new引用该类的构造器
BiFunction<String, Integer, ArrayTest> bif2 = ArrayTest :: new;
ArrayTest bArrayTest = bif2.apply("小蓝", 15);
//4.构造器引用创建数组
Function<Integer, String[]> funA1 = length -> new String[length];
Function<Integer, String[]> funA2 = String[] :: new;
String[] strArr = funA2.apply(10);
System.out.println(strArr.toString());
}
}
Lambda 使用示例
public class ArrayTest {
@Test
public void test1() {
//1消费型接口(提供一个参数根据需求实现进行消费没有返回值) Consumer<T> void accept(T t);
//解释在该类中有testConsumer(String str, Consumer<String>) 方法,
//该方法需要String与Consumer<String>两个参数,根据需求重写Consumer中的accept方法进行消费
//普通方式
this.testConsumer("aaa", new Consumer<String>() {
@Override
public void accept(String s) {
System.out.println("接口匿名实现消费输出:" + s);
}
});
//Lambda方式
//如果消费的需求不同,在调用testConsumer()方法时,根据需求定义不同的Lambda方法体
this.testConsumer("bbb", stringVal -> System.out.println("Lambde消费输出:" + stringVal));
//2断定型接口(提供一个类型参数,根据需求实现后返回一个Boolean值) Predicate<T> boolean test(T t);
//解释在该类中定义了filterString(List<String> list, Predicate<String> p)方法,
//根据需求不同重写Predicate中的test方法,返回真假,根据返回的真假结果,进行后续操作
List<String> strList = new ArrayList<>();
strList.add("ccc");
strList.add("cac");
strList.add("bb");
strList.add("aaa");
//普通方式(此处重写的test()方法,如果s中包含"a"返回true,执行filterString()中的代码,获取几号中包含"a"的字符串)
List<String> returnList1 = this.filterString(strList, new Predicate<String>() {
@Override
public boolean test(String s) {
return s.contains("a");
}
});
//Lambda方式(获取集合中包含"c"的字符串)
List<String> returnlist2 = this.filterString(strList, strVal -> strVal.contains("c"));
//3供给型接口(不提供参数,根据需求实现后返回一个指定类型参数) Supplier<T> T get();
Supplier<String> supplier2 = ArrayTest::existMethodReturn;
String s = supplier2.get();
//4函数型接口(提供一个类型参数,根据需求实现后返回另外一个类型的参数) Function<T,R> R apply(T t);
Function<Double, Long> fun1 = d -> Math.round(d);
//方法引用
Function<Double, Long> fun2 = Math::round;
fun2.apply(2.5);
}
//消费型,根据重写Consumer中accept()方法进行消费
public void testConsumer(String val1, Consumer<String> con) {
con.accept(val1);
}
//断定型遍历strList中的字符串,如果重写的Predicate接口中的test()方法为真,则将字符串放入新的集合中并返回
public List<String> filterString(List<String> strList, Predicate<String> pre) {
List<String> newList = new ArrayList<>();
for (String s : strList) {
if (pre.test(s)) {
newList.add(s);
}
}
return newList;
}
public static String existMethodReturn() {
return "已存在静态方法返回参数";
}
}
3. 再总结一下Lambda中几个函数
-
Runnable: 无入参,无返回值
-
Consumer 接受一个参数,没有返回值
-
Function<T, R> 接受一个参数,有返回值
-
Supplier 无入参,有返回值返回泛型类型数据
@FunctionalInterface
public interface Supplier<T> {
T get();
}
- BiConsumer 接受两个参数,没有返回值