Lambda表达式
函数式编程思想概述
在数学中,函数就是有输入量,输出量的一套计算方案,也就是拿数据做操作,面向对象思想强调,必须通过对象的形式来做事情,函数式思想则尽量忽略面向对象的复杂语法:强调做什么,而不是以什么形式去做,而Lambda表达式就是函数式思想的体现
体验Lambda表达式
需求:启动一个线程,在控制台输出一句话
方式1:
- 定义一个类MyRunnable实现Runnable接口,重写run()方法
- 创建MyRunnable类的对象
- 创建Thread类的对象,把MyRunnable的对象作为构造参数传递
- 启动线程
方式2:
- 匿名内部类的方式改进
方式3:
- Lambda表达式的方式改进
MyRunnable
public class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println("Here we go!!!");
}
}
LambdaDemo
public static void main(String[] args) {
//实现类的方式
MyRunnable runnable = new MyRunnable();
Thread t = new Thread(runnable);
t.start();
//使用匿名内部类的方式改进
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("I don't want to go");
}
}).start();
//Lambda表达式的方式
new Thread(() -> {
System.out.println("This is Lambda");
}).start();
}
Lambda表达式的标准格式
匿名内部类中重写run()方法的代码分析
- 方法形式参数为空,说明调用方法时不需要传递参数
- 方法返回值类型为void,说明方法执行没有结果返回
- 方法体中的内容,是我们具体要做的事情
Lambda表达式的代码分析
()
里面没有内容,可以看成是方法形式参数为空->
用箭头指向后面要做的事情{}
包含一段代码,我们称之为代码块,可以看成是方法体中的内容
组成Lambda表达式的三要素:形式参数
,箭头
,代码块
Lambda表达式的格式
- 格式:
(形式参数)->{代码块}
- 形式参数:如果有多个参数,参数之间用逗号隔开,如果没有参数,留空即可
->
:由英文中画线和大宇符号组成,固定写法,代表指向动作- 代码块:是我们具体要做的事情,也就是以前我们写的方法体内容
Lambda表达式的练习
Lambda表达式的使用前提
- 有一个接口
- 接口中有且仅有一个抽象方法
练习1
- 定义一个接口(Eatable),里面定义一个抽象方法:void eat()
定义一个测试类(EatableDemo),在测试类中提供两个方法
- 一个方法是:useEatable(Eatable e)
- 一个方法是主方法,在主方法中调用useEatable方法
Eatable
public interface Eatable {
void eat();
}
EatableImpl
public class EatableImpl implements Eatable {
@Override
public void eat() {
System.out.println("I can eat,i can't breath");
}
}
EatableDemo
public static void main(String[] args) {
//在主方法中调用useEatable
Eatable e = new EatableImpl();
useEatable(e);
//匿名内部类
useEatable(new Eatable() {
@Override
public void eat() {
System.out.println("I can't eat,i can't breath");
}
});
//Lambda表达式
useEatable(() -> {
System.out.println("I can't eat,but i can breath");
});
}
private static void useEatable(Eatable e) {
e.eat();
}
练习2
- 定义一个接口(Flyable),里面定义一个抽象方法:void fly(String s)
定义一个测试类(FlyableDemo),在测试类中提供两个方法
- 一个方法是:useFlyable(Flyable f)
- 一个方法是主方法,在主方法中调用useFlyable方法
Flyable
public interface Flyable {
void fly(String s);
}
FlyableDemo
public static void main(String[] args) {
//主方法中调用useFlyable
//匿名内部类
useFlyable(new Flyable() {
@Override
public void fly(String s) {
System.out.println(s);
System.out.println("dog can't fly,but can run");
}
});
System.out.println("---------");
//Lambda
useFlyable((String s) -> {
System.out.println(s);
System.out.println("cat can't fly too");
});
}
private static void useFlyable(Flyable f) {
f.fly("birds can fly");
}
练习3
- 定义一个接口(Addable),里面定义一个抽象方法:int add(int x,int y)
定义一个测试类(AddableDemo),在测试类中提供两个方法
- 一个方法是:useAddable(Addable a)
- 一个方法是主方法,在主方法中调用useAddable方法
Addable
public interface Addable {
int add(int x, int y);
}
AddableDemo
public static void main(String[] args) {
//匿名内部类
useAddable(new Addable() {
@Override
public int add(int x, int y) {
return x + y;
}
});
//Lambda
useAddable((int a, int b) -> {
//return a + b;
//所以要看它的实现方法是怎么样的,举例这里虽然是a,b,但我如果相减,返回的3-4=-1,相当于add方法是相减的
return a - b;
});
}
private static void useAddable(Addable a) {
int sum = a.add(3, 4);
System.out.println(sum);
}
Lambda表达式的省略模式
省略规则
- 参数类型可以省略,但是有多个参数的情况下,不能只省略一个
- 如果参数有且仅有一个,那么小括号可以省略
- 如果代码块的语句只有一条,可以省略大括号和分号,如果有return,甚至是return
LambdaDemo2
public static void main(String[] args) {
//参数的类型可以省略
useAddable((x, y) -> {
return x + y;
});
//如果参数有且仅有一个,那么小括号也可以省略
useFlyable(s -> {
System.out.println(s);
});
//如果代码块的语句只有一条,可以省略大括号和分号,如果有return,return也要省略
useFlyable(s -> System.out.println(s));
useAddable((x, y) -> x + y);
}
private static void useFlyable(Flyable f) {
f.fly("dog can't fly");
}
private static void useAddable(Addable a) {
int sum = a.add(3, 4);
System.out.println(sum);
}
Lambda表达式的注意事项
注意事项
- 使用Lambda必须要有接口,并且要求接口中有且仅有一个抽象方法
必须有上下文环境,才能推导出Lambda对应的接口
- 根据局部变量的赋值得知Lambda对应的接口:
Runnable r = () -> System.out.println("Lambda表达式");
- 根据调用方法的参数得知Lambda对应的接口:
new Thread(() -> System.out.println("Lambda表达式")).start();
- 根据局部变量的赋值得知Lambda对应的接口:
LambdaDemo3
public static void main(String[] args) {
//使用Lambda必须要有接口,并且要求接口中有且仅有一个抽象方法
useInter(() ->
System.out.println("I like fruits")
);
//必须要有上下文环境,才能推导出Lambda对应的接口
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("匿名内部类");
}
}).start();
Runnable r = () -> System.out.println("Lambda表达式");
new Thread(r).start();
new Thread(() -> System.out.println("Lambda表达式")).start();
}
private static void useInter(Inter i) {
i.show();
}
Lambda表达式和匿名内部类的区别
所需类型不同
- 匿名内部类:可以是接口,也可以是抽象类,还可以是具体类
- Lambda表达式:只能是接口
使用限制不同
- 如果接口中有且仅有一个抽象方法,可以使用Lambda表达式,也可以使用匿名内部类
- 如果接口中多于一个抽象方法,只能使用匿名内部类,而不能使用Lambda表达式
实现原理不同
- 匿名内部类:编译后,产生一个单独的
.class
字节码文件 - Lambda表达式:编译之后,没有一个单独的
.class
字节码文件,对应的字节码会在运行的时候动态生成
LambdaDemo4
public static void main(String[] args) {
//匿名内部类
useInter(new Inter() {
@Override
public void show() {
System.out.println("接口");
}
});
useAnimal(new Animal() {
@Override
public void method() {
System.out.println("抽象类");
}
});
useStudent(new Student() {
@Override
public void study() {
System.out.println("具体类");
}
});
System.out.println("--------");
//Lambda
useInter(() -> System.out.println("接口"));
}
private static void useStudent(Student s) {
s.study();
}
private static void useAnimal(Animal a) {
a.method();
}
private static void useInter(Inter i) {
i.show();
}
Inter
public interface Inter {
void show();
}
Animal
public abstract class Animal {
public abstract void method();
}
Student
public class Student {
public void study() {
System.out.println("I love study");
}
}
接口组成更新
接口组成更新描述
接口的组成
- 常量
public static final
- 抽象方法
public abstract
- 默认方法
Java 8
- 静态方法
java 8
- 似有方法
java 9
接口中默认方法
接口中默认方法的定义
- 格式:
public default 返回值类型 方法名(参数列表){}
- 例:
public default void show3(){}
接口中默认方法的注意事项
- 默认方法不是抽象方法,所以不强制被重写,但是可以被重写,重写的时候去掉default关键字
- public可以省略,default不能被省略
MyInterface
public interface MyInterface {
void show1();
void show2();
//Java8的默认方法
public default void show3() {
System.out.println("show3");
}
}
MyInterfaceImplOne
public class MyInterfaceImplOne implements MyInterface {
@Override
public void show1() {
System.out.println("One show1");
}
@Override
public void show2() {
System.out.println("One show2");
}
@Override
public void show3() {
System.out.println("One show3");
}
}
MyInterfaceImpTwo
public class MyInterfaceImpTwo implements MyInterface {
@Override
public void show1() {
System.out.println("Two show1");
}
@Override
public void show2() {
System.out.println("Two show2");
}
}
MyInterfaceDemo
public static void main(String[] args) {
MyInterface my1 = new MyInterfaceImplOne();
MyInterface my2 = new MyInterfaceImpTwo();
my1.show1();
my1.show2();
my1.show3();
my2.show3();
}
接口中静态方法
接口中静态方法的定义格式
- 格式:
public static 返回值类型 方法名(参数列表){}
- 范例:
public static void show(){}
接口中静态方法的注意事项
- 静态方法只能通过接口名调用,不能通过实现类名或者对象名调用
- public可以省略,static不能省略
Inter
public interface Inter {
void show();
default void method() {
System.out.println("Inter's default");
}
public static void test() {
System.out.println("Inter's static");
}
}
implements
public class InterImpl implements Inter {
@Override
public void show() {
System.out.println("Inter's show");
}
}
InterDemo
public static void main(String[] args) {
Inter i = new InterImpl();
i.show();
i.method();
Inter.test();
}
接口中私有方法
Java9中新增了带方法体的私有方法,这其实在Java8中就埋下了伏笔,Java8允许在接口中定义带方法体的默认方法和静态方法,这样可能就会引发一个问题:当两个默认方法或者静态方法中包含一段相同的代码实现时,程序必然考虑将这段实现代码抽取成一个共性方法,而这个共性方法是不需要让别人使用的,因此用私有给隐藏起来,这就是Java9增加私有方法的必然性
接口中私有方法的定义格式
- 格式1:
private 返回值类型 方法名(参数列表){}
- 范例1:
private void show(){}
- 格式2:
private static 返回值类型 方法名(参数列表){}
- 范例2:
private static void method(){}
接口中私有方法的注意事项
- 默认方法可以调用私有的静态方法和非静态方法
- 静态方法只能调用私有的静态方法
方法引用
体验方法引用
在使用Lambda表达式的时候,我们实际上传递进去的代码就是一种解决方案:拿参数做操作,那么考虑一种情况:如果我们在Lambda中所指定的操作方案,已经有地方存在相同方案,那是否还有必要再写重复逻辑呢?答案肯定是没有必要,那我们又是如何使用已经存在的方案的呢?
Printable
public interface Printable {
void printString(String s);
}
PrintableDemo
public static void main(String[] args) {
usePrintable((String s) -> {
System.out.println(s);
});
//方法引用符号: ::
usePrintable(System.out::println);
}
private static void usePrintable(Printable p) {
p.printString("I'm so tired");
}
方法引用符
方法引用符
::
该符号为引用符号,而它所在的表达式被称为方法引用
回顾一下我们在体验方法引用的代码
- Lambda表达式:拿到参数s之后通过Lambda表达式,传递给System.out.println方法去处理
- 方法引用:直接使用System.out中的println方法来取代Lambda,代码更加的简洁
推导与省略
- 如果使用Lambda,那么根据可推导就是可省略的原则,无需指定参数类型,也无需指定重载形式,它们都将被自动推导
- 如果使用方法引用,也是同样可以根据上下文进行推导
- 方法引用是Lambda的孪生兄弟
Lambda表达式支持的方法引用
常见的引用方法
- 引用类方法
- 引用对象的实例方法
- 引用类的实例方法
- 引用构造器
引用类方法
引用类方法,其实就是引用类的静态方法
- 格式:
类名::静态方法
- 范例:
Integer::parseInt
,Integer类的方法:public static parseInt(String s)
将此String转换为int类型数据
练习:
- 定义一个接口(Converter),里面定义一个抽象方法:int convert(String s);
- 定义一个测试类(ConverterDemo),在测试类中提供两个方法,一个方法是:useConverter(Converter c),一个方法是主方法,在主方法中调用useConverter方法
Converter
public interface Converter {
int convert(String s);
}
ConverterDemo
public static void main(String[] args) {
//使用Lambda表达式
useConverter(s -> Integer.parseInt(s));
//引用类方法
useConverter(Integer::parseInt);
//Lambda表达式被类方法替代的时候,它的形式参数全部传递给静态方法作为参数
}
private static void useConverter(Converter c) {
int i = c.convert("6324");
System.out.println(i);
}
引用对象的实例方法
引用对象的实例方法,其实就引用类中的成员方法
- 格式:
对象::成员方法
- 范例:
HellowWorld::toUpperCase
,String类中的方法:public String toUpperCase()
将此String所有字符转换为大写
练习
- 定义一个类(PrintString),里面定义一个方法:public void printUpper(String s):把字符串参数变成大写的数据,然后在控制台输出
- 定义一个接口(Printer),里面定义一个抽象方法:void printUpperCase(String s)
- 定义一个测试类(PrinterDemo),在测试类中提供两个方法,一个方法是:usePrinter(Printer p),一个方法是主方法,在主方法中调用usePrinter方法
PrintString
public class PrintString {
public void printUpper(String s) {
String result = s.toUpperCase();
System.out.println(result);
}
}
Printer
public interface Printer {
void printUpperCase(String s);
}
PrinterDemo
public static void main(String[] args) {
usePrinter((String s) -> {
String result = s.toUpperCase();
System.out.println(result);
});
//引用对象的实例方法
PrintString ps = new PrintString();
usePrinter(ps::printUpper);
//Lambda表达式被对象的实例方法替代的时候,它的形式参数全部传递给该方法作为参数
}
private static void usePrinter(Printer p) {
p.printUpperCase("HelloWorld");
}
引用类的实例方法
引用类的实例方法,其实就是引用类中的成员方法
- 格式:
类名::成员方法
- 范例:
String::substring
,String类中的方法:public String substring(int beginIndex,int endIndex)
从beginIndex开始到endIndex结束,截取字符串,返回一个子串,子串的长度为endIndex-beginIndex
练习
- 定义一个接口(MyString),里面定义一个抽象方法:String mySubString(String s,int x,int y)
- 定义一个测试类(MyStringDemo),在测试类中提供两个方法,一个方法是:useMyString(MyString my),一个方法是主方法,在主方法中调用useMyString方法
MyString
public interface MyString {
String mySubString(String s, int x, int y);
}
MyStringDemo
public static void main(String[] args) {
useMyString((String s, int x, int y) -> {
return s.substring(x, y);
});
//引用类中的实例方法
useMyString(String::substring);
//Lambda表达式被类的实例方法替代的时候,第一个参数作为调用者,后面的参数全部传递给该方法作为参数
}
private static void useMyString(MyString my) {
String s = my.mySubString("Where are you now", 3, 12);
System.out.println(s);
}
引用构造器
引用构造器,其实就是引用构造方法
- 格式:
类名::new
- 范例:
Student::new
练习
- 定义一个类(Student),里面有两个成员变量(name,age),并提供无参构造方法和带参构造方法,以及成员变量对应的get和set方法
- 定义一个接口(StudentBuilder),里面定义一个抽象方法Student build(String name,int age)
- 定义一个测试类(StudentDemo),在测试类中提供两个方法,一个方法是:useStudentBuilder(StudentBuilder s),一个方法是主方法,在主方法中调用useStudentBuilder方法
Student
public class Student {
private String name;
private int age;
public Student() {
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
StudentBuilder
public interface StudentBuilder {
Student build(String name, int age);
}
StudentDemo
public static void main(String[] args) {
useStudentBuilder((String name, int age) -> {
return new Student(name, age);
});
//引用构造器
useStudentBuilder(Student::new);
//Lambda表达式被构造器替代的时候,它的形式参数全部传递给构造器作为参数
}
private static void useStudentBuilder(StudentBuilder sb) {
Student s = sb.build("YuKi", 21);
System.out.println(s.getName() + " " + s.getAge());
}
函数式接口
函数式接口概述
函数式接口:有且仅有一个抽象方法的接口
Java中的函数式编程体现就是Lambda表达式,所以函数式接口就是可以适用于Lambda使用的接口,只有确保接口中有且仅有一个抽象方法,Java中的Lambda才能顺利的进行推导
如何检测一个接口是不是函数式接口?
@FunctionalInterface
- 放在接口定义的上方:如果接口是函数式接口,编译通过,如果不是,编译失败
注意
- 我们自己定义函数式接口的时候,
@FunctionalInterface
是可选的,就算我不写这个注解,只要保证满足函数式接口定义的条件,也照样是函数式接口,但是建议加上
该注解
函数式接口作为方法的参数
需求
- 定义一个类(RunnableDemo),在类中提供两个方法,一个方法是:startThread(Runnable r),方法参数Runnable是一个函数式接口,一个方法是主方法,在主方法中调用startThread方法
如果方法的参数是一个函数式接口,我们可以使用Lambda表达式作为参数传递
RunnableDemo
public static void main(String[] args) {
//匿名内部类的方式
startThread(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "线程启动了");
}
});
//Lambda表达式
startThread(() -> System.out.println(Thread.currentThread().getName() + "线程启动了"));
}
private static void startThread(Runnable r) {
new Thread(r).start();
}
函数式接口作为方法的返回值
需求
- 定义一个类(ComparatorDemo),在类中提供两个方法,一个方法是:
Comparator<String> getComparator()
,方法返回值Comparator是一个函数式接口,一个方法是主方法,在主方法中调用getComparator方法
如果方法的返回值是一个函数式接口,我们可以使用Lambda表达式作为结果返回
ComparatorDemo
public static void main(String[] args) {
//构造使用场景
//定义集合,存储字符串元素
ArrayList<String> array = new ArrayList<>();
array.add("YuKi");
array.add("XP");
array.add("XianKe");
array.add("LaoWang");
System.out.println("排序前" + array);
Collections.sort(array, getComparator());
System.out.println("排序后" + array);
}
private static Comparator<String> getComparator() {
//匿名内部类的方式实现
// return new Comparator<String>() {
// @Override
// public int compare(String o1, String o2) {
// return o1.length() - o2.length();
// }
// };
//Lambda表达式
return (s1, s2) -> s1.length() - s2.length();
}
常见的函数式接口
Java8在java.util.function
包下预定义了大量的函数式接口供我们使用
重点了解这几个
- Supplier接口
- Consumer接口
- Predicate接口
- Function接口
Supplier接口
Supplier<T>
:包含一个无参的方法
- T get():获得结果
- 该方法不需要参数,它会按照某种实现逻辑(由Lambda表达式实现)返回一个数据
Supplier <T>
:接口也被称为生产型接口,如果我们指定了接口的泛型是什么类型,那么接口中的get方法就会生产什么类型的数据供我们使用
SupplierDemo
public static void main(String[] args) {
String s = getString(() -> {
return "YuKi";
});
System.out.println(s);
Integer i = getInteger(() -> 6324);
System.out.println(i);
}
//定义一个方法,返回一个整数数据
private static Integer getInteger(Supplier<Integer> sup) {
return sup.get();
}
//定义一个方法,返回一个字符串数据
private static String getString(Supplier<String> sup) {
return sup.get();
}
练习
- 定义一个类(SupplierTest),在类中提供两个方法,一个方法是:
int getMax(Supplier <Integer>) sup
,用于返回一个int数组中的最大时,一个方法是主方法,在主方法中调用getMax方法
SupplierTest
public static void main(String[] args) {
//定义一个数组
int[] arr = {23, 12, -2, 134, 289};
int result = getMax(() -> {
int max = arr[0];
for (int i = 1; i < arr.length; i++) {
if (arr[i] > max) {
max = arr[i];
}
}
return max;
}
);
System.out.println(result);
}
/**
* 返回一个int数组中的最大值
*/
private static int getMax(Supplier<Integer> sup) {
return sup.get();
}
Consumer接口
Consumer<T>
:包含两个方法
void accept(T t)
:对给定的参数执行此操作default Consumer<T> andThen(Consumer after)
:返回一个组合的Consumer,依次执行此操作,然后执行after操作Consumer<T>
接口也被称为消费型接口,它消费的数据的数据类型由泛型指定
ConsumerDemo
public static void main(String[] args) {
operatorString("YuKi", (String s) -> {
System.out.println(s);
});
operatorString("YuKi", System.out::println);
operatorString("XP", s -> {
System.out.println(new StringBuilder(s).reverse().toString());
});
System.out.println("-----------");
operatorString("YuKi", s -> System.out.println(s),
s -> System.out.println(new StringBuilder(s).reverse().toString()));
}
/**
* 定义一个方法,消费一个字符串数据
*/
private static void operatorString(String name, Consumer<String> con) {
con.accept(name);
}
/**
* 定义一个方法,用不同的方式消费同一个字符串两次
*/
private static void operatorString(String name, Consumer<String> con1, Consumer<String> con2) {
//这里可以无限的跟 a.andThen(b).andThen(c).andThen(d).accept
con1.andThen(con2).accept(name);
}
练习
- String[] strArray={"YuKi,21","XP,21","XianKe,22"}
- 字符串数组中有多条信息,请按照格式:"姓名:XX,年龄:XX"的格式将信息打印出来
- 要求:把打印姓名的动作作为第一个Consumer接口的Lambda实例,把打印年龄的动作作为第二个Consumer接口的Lambda实例,将两个Consumer接口按照顺序组合到一起使用
ConsumerTest
public static void main(String[] args) {
String[] strArray = {"YuKi,21", "XP,21", "XianKe,22"};
printInfo(strArray, (String s) -> {
String name = s.split(",")[0];
System.out.print("姓名:" + name + ",");
}, (String s) -> {
String age = s.split(",")[1];
System.out.println("年龄:" + age);
});
}
private static void printInfo(String[] strArray, Consumer<String> con1, Consumer<String> con2) {
for (String s : strArray) {
con1.andThen(con2).accept(s);
}
}
Predicate接口
Predicate<T>
:常用的四个方法
boolean test(T t)
:对给定的参数进行判断(判断逻辑由Lambda表达式实现),返回一个布尔值default Predicate<T> negate()
:返回一个逻辑的否定,对应逻辑非default Predicate<T> and(Predicate other)
:返回一个组合判断,对应短路与default Predicate<T> or(Predicate other)
:返回一个组合判断,对应短路或Predicate<T>
接口通常用于判断参数是否满足指定的条件
PredicateDemo1
public static void main(String[] args) {
boolean b1 = checkString("YuKi", (String s) -> {
return s.length() > 5;
});
System.out.println(b1);
boolean b2 = checkString("Hello world", s -> s.length() > 5);
System.out.println(b2);
boolean b3 = isString("Hello world", s -> s.length() > 5);
System.out.println(b3);
}
/**
* 判断给定的字符串是否满足要求
*/
private static boolean checkString(String s, Predicate<String> pre) {
return pre.test(s);
}
private static boolean isString(String s, Predicate<String> pre) {
return pre.negate().test(s);
}
PredicateDemo2
public static void main(String[] args) {
boolean b1 = checkString("YuKi", s -> s.length() > 5, s -> s.length() < 10);
System.out.println(b1);
boolean b2 = orString("YuKi", s -> s.length() < 5, s -> s.length() > 15);
System.out.println(b2);
}
/**
* 同一个字符串给出两个不同的判断条件,最后把这两个判断的结果做逻辑与运算的结果作为最终的结果
*/
private static boolean checkString(String s, Predicate<String> pre1, Predicate<String> pre2) {
return pre1.and(pre2).test(s);
}
private static boolean orString(String s, Predicate<String> pre1, Predicate<String> pre2) {
return pre1.or(pre2).test(s);
}
练习
- String[] strArray={"YuKi,21","XP,21","LaoWang,21","XianKe,22"}
- 字符串数组中有多条信息,请通过Predicate接口的拼装将符合要求的字符串筛选到集合ArrayList中,并遍历ArrayList集合
- 同时满足如下要求:姓名长度大于2,年龄大于21
- 分析:有两个判断条件,所以需要使用两个Predicate接口,对条件进行判断,必须同时满足两个条件,所以可以使用and方法连接两个判断条件
PredicateTest
public static void main(String[] args) {
String[] strArray = {"YuKi,21", "XP,21", "LaoWang,21", "XianKe,22"};
ArrayList<String> person = myFilter(strArray, s -> s.split(",")[0].length() > 2,
s -> Integer.parseInt(s.split(",")[1]) > 21
);
for (String s : person) {
System.out.println(s);
}
}
/**
* 通过Predicate接口的拼装将符合要求的字符串筛选到集合ArrayList中
*/
private static ArrayList<String> myFilter(String[] strArray, Predicate<String> pre1,
Predicate<String> pre2) {
//定义一个集合
ArrayList<String> array = new ArrayList<>();
for (String s : strArray) {
if (pre1.and(pre2).test(s)) {
array.add(s);
}
}
return array;
}
Function接口
Function<T,R>
:常用的两个方法
R apply(T t)
:将此函数应用于给定的参数default <V> Function andThen(Function after)
:返回一个组合函数,首先将该函数应用于输入,然后将after函数应用于结果Function<T,R>
接口通常用于对参数进行处理,转换(处理逻辑由Lambda表达式实现),然后返回一个新的值
FunctionDemo
public static void main(String[] args) {
convert("32", s -> Integer.parseInt(s));
convert(20, i -> String.valueOf(i + 3));
convert("62", s -> Integer.parseInt(s) + 3, integer -> String.valueOf(integer));
}
/**
* 定义一个方法,把一个字符串转化int类型,在控制台输出
*/
private static void convert(String s, Function<String, Integer> fun) {
Integer i = fun.apply(s);
System.out.println(i);
}
/**
* 定义一个方法,把一个int类型的数据加上一个整数之后,转为字符串在控制台输出
*/
private static void convert(Integer i, Function<Integer, String> fun) {
String s = fun.apply(i);
System.out.println(s);
}
/**
* 定义一个方法,把一个字符串转换为int类型,把int类型的数据加上一个整数之后,再转为字符串在控制台输出
*/
private static void convert(String s, Function<String, Integer> fun1,
Function<Integer, String> fun2) {
String str = fun1.andThen(fun2).apply(s);
System.out.println(str);
}
练习
- String s="YuKi,21"
请按照要求进行操作
- 将字符串截取到数字年龄部分
- 将上一步的年龄字符串转换成int类型的数据
- 将上一步的int数据加50,得到一个int结果,在控制台输出
- 请通过Function接口来实现函数拼接
FunctionTest
public static void main(String[] args) {
String s = "YuKi,21";
convert(s, s1 -> s1.split(",")[1], s1 -> Integer.parseInt(s1), i -> i + 50);
}
private static void convert(String s, Function<String, String> fun1,
Function<String, Integer> fun2, Function<Integer, Integer> fun3) {
Integer ss = fun1.andThen(fun2).andThen(fun3).apply(s);
System.out.println(ss);
}