Lambda表达式
引入函数式编程风格:
在以前调用Java方法的时候,实参必须是一个基本数据类型的值或者是一个对象,现在可以给一些引用数据类型的形参,传一段代码而不是一个对象(这段代码是一个函数的主体方法)。
Lambda表达式的好处:
可以极大地减少代码冗余,同时可读性也好过冗长的匿名内部类
并不是所有的引用数据类型都可以使用Lambda表达式,只有少部分可以使用
SAM(函数式接口)类型的形参,变量才可以赋值为Lambda表达式
函数式接口:只有一个抽象方法的接口,这个接口可以有默认方法和静态方法成员等
结论:
1.只有函数式接口才能使用Lambda表达式
2.Lambda作用就是简化代码,使代码更整洁
例如:
public class Teacher {
private int age;
private String name;
public Teacher() {}
public Teacher(int age, String name) {
this.age = age;
this.name = name;
}
@Override
public String toString() {
return "Teacher{" +
"age=" + age +
", name='" + name + '\'' +
'}';
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
TreeSetadd的对象必须实现Comparator接口,否则就在调用时写入,或者用Lambda写入。
public class TestLambda {
@Test
public void test4(){
// TreeSet 中添加Teacher对象 Lambda表达式
TreeSet<Teacher> teachers = new TreeSet<>((teacher1,teacher2) -> teacher1.getAge() - teacher2.getAge());
teachers.add(new Teacher());
teachers.add(new Teacher(18,"Y"));
for (Teacher teacher : teachers
) {
System.out.println(teacher);
}
}
@Test
public void test3(){
// TreeSet 中添加Teacher对象
TreeSet<Teacher> teachers = new TreeSet<>(new Comparator<Teacher>() { // 未在Teacher中实现Comparator接口 在这里传
@Override
public int compare(Teacher teacher, Teacher t1) {
return t1.getAge() - teacher.getAge();
}
});
teachers.add(new Teacher());
teachers.add(new Teacher(18,"Y"));
for (Teacher teacher : teachers
) {
System.out.println(teacher);
}
}
@Test
public void test2(){
// Lambda创建线程
new Thread(() -> System.out.println("Lambda")).start();
}
@Test
public void test1(){
// 匿名内部类创建线程
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("匿名内部类");
}
}).start();
}
}
函数式接口:
SAM (Single Abstract Method),即该接口中有且仅有一个抽象方法需要实现(当然该方法可以包含其他非抽象方法)
学习的有且仅有一个抽象方法的接口:
- java.lang.Runnable: public void run()
- java.lang.Comparable :public int compareTo(T t)
- java.util.Comparator :public int compare(T t1, T t2)
- java.lang.Iterable:public Iterator iterator()
- java.lang.reflect.InvocationHandler:public Object invoke(Object proxy,Method method,Object[] args)
- java.io.FileFilter:public boolean accept(File pathname)
如上接口中,都符合有且仅有一个抽象方法,但在JDK1.8之后建议在所有函数式接口添加注解@FunctionalInterface
而没有添加的说明在以后的JDK版本更新时,可能会添加抽象方法,现在用Lambda表达式没问题,但JDK更新之后不确定
Runnable、Comparator、FileFilter都是确定的函数式接口
public class TestFunctionalInterface {
@Test
public void test2(){
File file = new File("D:\\IOtest\\123456\\12\\5\\123"); //是一个文件夹
// 遍历文件夹中以.docx结尾的文件
File[] files = file.listFiles((file1) -> file1.getName().endsWith(".docx"));//Lambda表达式:(传参) -> 结果
for (File f : files
) {
System.out.println(f);
}
}
@Test
public void test(){
File file = new File("D:\\IOtest\\123456\\12\\5\\123"); //是一个文件夹
// 遍历文件夹中以.docx结尾的文件
File[] files = file.listFiles(new FileFilter() {
@Override
public boolean accept(File file) {
if (file.getName().endsWith(".docx"))
return true;
return false;
}
});
for (File f : files
) {
System.out.println(f);
}
}
}
@FunctionalInterface
interface MyInterface{
void test();
}
JDK1.8新增了很多包,其中一个包:java.lang.funtion
提供了函数式接口
这些接口主要可以分为四大类:
- 1.消费型接口(Consumer):
它的抽象方法有参无返回值 - 2.供给型接口(Supplier):
无参有返回值 - 3.判断型接口(Predicate):
无论什么参数,输出的都是true或者false - 4.功能型接口(Function):
有参有返回值
消费型接口:
- 有参无返回值
- 最基本的代表:Consumer : void accept(T t)
- 例如:集合java.util.Collection系列在JDK1.8之后就增加了这样的方法:
- default void forEach(Consumer<? super T> action)
public class TestConsumer {
@Test
public void test(){
// 创建一个集合
ArrayList<String> arrayList = new ArrayList<>();
arrayList.add("你好");
arrayList.add("世界");
arrayList.add("java");
arrayList.add("Lambda");
// 遍历集合:
// for (String s : arrayList
// ) {
// System.out.println(s);
// }
// 用forEach方法遍历 Lambda表达式
arrayList.forEach((string) -> System.out.println(string));
}
}
供给型接口:
无参有返回值
最基本的代表:
Supplier :T get()
举例:
StreamAPI:
- Stream是一个数据流, java.util.stream包的Stream类型
static <T> Stream<T> generate(Supplier<T> s)
public class TestSupplier {
@Test
public void test(){
// generate产生
// Math.random():随机数
Stream<Double> stream = Stream.generate(() -> Math.random());
// 遍历 Lambda表达式
stream.forEach((d) -> System.out.println(d));
}
}
判断型接口
抽象方法:boolean test(参数)
最基本代表:
Predicate boolean test(T t)
举例:
- java.util.Collection
default boolean removeIf(Predicate<? super E> filter)
public class TestPredicate {
@Test
public void test(){
ArrayList<Integer> arrayList = new ArrayList<>();
for (int i = 1; i <=10; i++) {
arrayList.add(i);
}
// 移除三和三的倍数,并遍历
// arrayList.remove(3); 太麻烦
// arrayList.remove(6);
// arrayList.remove(9);
// 方法二:迭代器
// Iterator<Integer> iterator = arrayList.iterator();
// while (iterator.hasNext()){
// Integer num = iterator.next();
// if (num % 3 == 0){
// iterator.remove();
// }
// }
// for (Integer i : arrayList
// ) {
// System.out.println(i);
// }
// 方法三
arrayList.removeIf((integer) -> integer%3 == 0);// 如果是三的倍数,即remove
for (Integer i : arrayList
) {
System.out.println(i);
}
}
}
lambda表达式语法:
Lambda表达式就是给函数式接口的形参或变量赋值用的,为了给这个函数式接口的抽象方法传递代码用
- 语法格式:
(形参列表) -> {Lambda体}
(形参列表)就是函数式接口的抽象方法的形参列表
{lambda体}就是函数式接口的抽象方法的方法体 - 说明:
1.当形参列表是空参时,那么()不能省略
2.当形参列表是非空参时,并且数据类型是确定的或者可以推断的,那么形参的数据类型可以省略
3.当形参列表是非空参时,并且只有一个形参,且类型也省略了,那么此时()也可以省略,如果类型没有省略,()也不能省略
4.如果{lambda}不只有一个语句,那么{}不能省略,并且每一句都要加;
5.如果{lambda}只有一个语句,那么{}和;都可以省略(同时省略)
6.如果该函数式接口的抽象方法的返回值类型不是void,那么{lambda体}里要有return 返回值语句; 当{lambda}中只有一个{return 返回值;}时,{}、;、return都同时省略
public class TestLambdaGrammar {
@Test
public void test(){
// 创建一个集合
ArrayList<String> arrayList = new ArrayList<>();
arrayList.add("你好");
arrayList.add("世界");
arrayList.add("java");
arrayList.add("Lambda");
arrayList.forEach(new Consumer<String>() {// 匿名内部类方式
// 用匿名内部类方式给Consumer接口的形参赋值
@Override
public void accept(String s) {
System.out.println(s);
}
});
// 用forEach方法遍历 Lambda表达式 完整版
arrayList.forEach((String string) -> {System.out.println(string);});
}
}
Lambda表达式练习
1.使用forEach(Consumer c)方法便利Collection系列集合
创建一个ArrayList集合,保存Teacher对象,用上述方法遍历
public class TestExer1 {
public static void main(String[] args) {
ArrayList<Teacher> teachers = new ArrayList<>();
teachers.add(new Teacher(24,"Sam"));
teachers.add(new Teacher(25,"Ann"));
teachers.add(new Teacher(21,"Blue"));
teachers.add(new Teacher(24,"DiDi"));
teachers.add(new Teacher(26,"YuWei"));
teachers.forEach(teacher -> System.out.println(teacher));
}
}
2.使用removeIf(Predicate p) 删除元素
创建一个ArrayList集合,储存Teacher对象,删除年龄大于25的教师
public class TestExer2 {
public static void main(String[] args) {
ArrayList<Teacher> teachers = new ArrayList<>();
teachers.add(new Teacher(24,"Sam"));
teachers.add(new Teacher(25,"Ann"));
teachers.add(new Teacher(21,"Blue"));
teachers.add(new Teacher(24,"DiDi"));
teachers.add(new Teacher(26,"YuWei"));
// 删除年龄大于25的教师
teachers.removeIf(teacher -> teacher.getAge()>25);
// lambda遍历
teachers.forEach(teacher -> System.out.println(teacher));
}
}
3.(1)教师类实现Comparable接口 按年龄排序并遍历
(2)按姓名首字母排序
public class TestExer3 {
@Test
public void test1(){
TreeSet<Teacher> teachers = new TreeSet<>();
teachers.add(new Teacher(24,"Sam"));
teachers.add(new Teacher(25,"Ann"));
teachers.add(new Teacher(21,"Blue"));
teachers.add(new Teacher(24,"DiDi"));
teachers.add(new Teacher(26,"YuWei"));
// lambda遍历
teachers.forEach(teacher -> System.out.println(teacher));
}
@Test
public void test2(){
// 按名字首字母排列
TreeSet<Teacher> teachers = new TreeSet<>((t1,t2) -> t1.getName().compareToIgnoreCase(t2.getName()));
teachers.add(new Teacher(24,"Sam"));
teachers.add(new Teacher(25,"Ann"));
teachers.add(new Teacher(21,"Blue"));
teachers.add(new Teacher(24,"DiDi"));
teachers.add(new Teacher(26,"YuWei"));
// lambda遍历
teachers.forEach(teacher -> System.out.println(teacher));
}
}
4.创建一个HashMap,key是你的名字,value是你对象的名字
使用Map的default void forEach()方法遍历
import java.util.HashMap;
public class TestExer4 {
public static void main(String[] args) {
HashMap<String,String> names = new HashMap<>();
names.put("YYX","DLQ");
names.forEach((yyx,dlq) -> System.out.println("我是" + yyx + "我的女朋友是" + dlq));
}
}
感谢段段