稀土掘金 稀土掘金

我使用 Lambda 表达式将上百行的代码精简到几行

关注微信公众号【Java之言】,领取学习资料和视频,助你放弃编程之路!

系列文章目录

序号文章链接
1 玩转 Java8 Stream,让你代码更高效紧凑简洁
2 玩转 Java8 Optional,让你代码更紧凑简洁且不再出现空指针

一、Lambda 简介

Lambda 表达式,也可称为闭包,是一个匿名函数。我们可以把 Lambda 表达式理解为是一段可传递的代码(像数据一样传递)。即 Lambda 允许把函数作为一个方法的实参参数(函数当作参数传递到方法中)。是Java8的其中一个很重要的新特性。

从而可以写出更简洁,更灵活的代码。作为一种更加紧凑的代码风格,使java的语言表达能力得到了提升。

public class LambdaDemo {
    public static void main(String[] args) {
        List<String> list = Arrays.asList("A", "B", "C");
        //x -> System.out.println(x),就是一个匿名函数,即Lambda表达式,作为实参传给dealList方法
        dealList(list, x -> System.out.println(x));
    }
	
    public static void dealList(List<String> list, Consumer<String> consumer) {
    	// 遍历list中的每一个元素,传给consumer对象的accept函数,进行调用
        for (String x : list) {
            consumer.accept(x);
        }
    }
}

Lambda 表达式是对某些接口的简单实现,但不是所有接口都可以使用 Lambda 表达式来实现的,Lambda 规定能被 Lambda 表达式实现的接口中,它只能只有一个需要被实现的方法(函数),但不要求接口中只能只有一个方法。因为Java8中有另外一个新特性,即 default 关键字修饰的接口方法有默认实现,这个默认的方法是可以不需要子类实现的,可使用@FunctionalInterface注解来强制使接口只能有一个需要被实现的方法。

// 此注解表明此接口为函数接口,即只能有一个抽象方法
@FunctionalInterface
public interface Human {

    // 抽象方法,需要被实现
    void eat(String name);

    // default修饰的默认方法,不需要被子类实现
    default void run() {
        System.out.println("I can run...");
    }
}
public static void main(String[] args) {
    Human human = x -> System.out.println(x + " is eat");
    human.eat("Mr.nobody");
    human.run();
}
// 输出结果
Mr.nobody is eat
I can run...

二、Lambda 优势

2.1 案例一

以前我们要写一个类才能实现一个接口,并且实现里面的抽象方法。如方式一:

// 接口
public interface Human {
	// 抽象方法,需要被实现
    void speak();
}
// 实现类,并且实现抽象方法
public class Man implements Human {
    @Override
    public void speak() {
        System.out.println("I am man!");
    }
}
public class Main {
    public static void main(String[] args) {
        // 方式一 直接编写实现类
        Human human = new Man();
        human.speak();
    }
}

如果我们不想编写一个单独的实现类(Man),则可以用匿名内部类。如方式二:

public class Main {
    public static void main(String[] args) {
        // 方式一 直接编写实现类
        Human human = new Man();
        human.speak();

        // 方式二 匿名内部类
        Human human1 = new Human() {
            @Override
            public void speak() {
                System.out.println("I am woman!");
            }
        };
        human1.speak();
    }
}

但方式二中,有用的就只有System.out.println("I am woman!");这一行,所以有了Lambda表达式,可以这样写,如方式三:

public class Main {
    public static void main(String[] args) {
        // 方式一 直接编写实现类
        Human human = new Man();
        human.speak();
        
        // 方式二 匿名内部类
        Human human1 = new Human() {
            @Override
            public void speak() {
                System.out.println("I am woman!");
            }
        };
        human1.speak();
        
        // 方式三 Lambda表达式
        Human human2 = () -> System.out.println("I am woman!");
        human2.speak();
    }
}

2.2 案例二

再假如我们要对一个Student类的数组按指定条件进行过滤,如下:

public class Student {
    private String name;
    private int age;
    private double score;
}
public class Main {

    public static void main(String[] args) {

        List<Student> students = Arrays.asList(new Student("张三", 18, 89.5),
                new Student("李四", 20, 60), new Student("王五", 19, 100), new Student("赵六", 22, 89));

        // 过滤出年龄大于等于20的学生
        List<Student> stus1 = filterStudentByAge(students);
        System.out.println(stus1);

        // 过滤出成绩大于80的学生
        List<Student> stus2 = filterStudentByScore(students);
        System.out.println(stus2);
    }

    // 过滤出年龄大于等于20的学生
    private static List<Student> filterStudentByAge(List<Student> students) {
        List<Student> stus = new ArrayList<>();
        for (Student stu : students) {
            if (stu.getAge() >= 20) {
                stus.add(stu);
            }
        }
        return stus;
    }

    // 过滤出成绩大于80的学生
    private static List<Student> filterStudentByScore(List<Student> students) {
        List<Student> stus = new ArrayList<>();
        for (Student stu : students) {
            if (stu.getScore() > 80) {
                stus.add(stu);
            }
        }
        return stus;
    }
}

按上面的方式,如果要按另外一个条件过滤呢,又要写一个方法。那可以用策略模式处理,编写一个抽象策略接口,然后编写多个不同策略类实现它。

// 策略接口
public interface MyPredicate<T> {
    boolean test(T t);
}
// 过滤出年龄大于等于20的学生
public class filterStudentByAge implements MyPredicate<Student> {
    @Override
    public boolean test(Student t) {
        return t.getAge() >= 20;
    }
}
// 过滤出成绩大于80的学生
public class filterStudentByScore implements MyPredicate<Student> {
    @Override
    public boolean test(Student t) {
        return t.getScore() > 80;
    }
}
public class Main {

    public static void main(String[] args) {

        List<Student> students = Arrays.asList(new Student("张三", 18, 89.5),
                new Student("李四", 20, 60), new Student("王五", 19, 100), new Student("赵六", 22, 89));

        // 过滤出年龄大于等于20的学生
        List<Student> stus1 = filterStudent(students, new FilterStudentByAge());
        System.out.println(stus1);

        // 过滤出成绩大于80的学生
        List<Student> stus2 = filterStudent(students, new FilterStudentByScore());
        System.out.println(stus2);
    }

    // 按myPredicate策略过滤出满足条件的学生
    private static List<Student> filterStudent(List<Student> students,
            MyPredicate<Student> myPredicate) {
        List<Student> stus = new ArrayList<>();
        for (Student stu : students) {
            if (myPredicate.test(stu)) {
                stus.add(stu);
            }
        }
        return stus;
    }
}

但是以上方式,每增加一个过滤条件,就要编写一个策略类,太麻烦。所以我们就用匿名内部类方式。

// 匿名内部类形式 过滤出年龄大于等于18的学生
List<Student> stus3 = filterStudent(students, new MyPredicate<Student>() {
    @Override
    public boolean test(Student t) {
        return t.getAge() > 18;
    }
});
System.out.println(stus3);

但我们会觉得匿名内部类还是太麻烦,无用代码太多,有用的代码其实就只有return t.getAge() > 18;,于是 Lambda 表达式发挥的作用就来了:

// Lambda形式 过滤出年龄大于等于18的学生
List<Student> stus4 = filterStudent(students, t -> t.getAge() > 18);
System.out.println(stus4);

这时还是有人会问,那我们定义的接口MyPredicate方法filterStudent(),好像没什么作用呀。然而官方已经想到这一点,它内置了一些通用接口,我们可以使用它。例如断言的接口 Predicate,那我们就用如下方式,完全不用写接口MyPredicate方法filterStudent(),如下:

// 按myPredicate策略过滤出满足条件的学生
private static List<Student> filterStudent(List<Student> students,
        Predicate<Student> predicate) {
    List<Student> stus = new ArrayList<>();
    for (Student stu : students) {
        if (predicate.test(stu)) {
            stus.add(stu);
        }
    }
    return stus;
}

当然,如果你会使用Stream(可以看我另外一篇文章),只需要写下面的代码,如下:

public class Main {

    public static void main(String[] args) {

        List<Student> students = Arrays.asList(new Student("张三", 18, 89.5),
                new Student("李四", 20, 60), new Student("王五", 19, 100), new Student("赵六", 22, 89));

        // 过滤出年龄大于等于20的学生
        students.stream().filter(t -> t.getAge() >= 20).forEach(System.out::println);
        
        System.out.println("-------------------------------------");

        // 过滤出成绩大于80的学生
        students.stream().filter(t -> t.getScore() > 80).forEach(System.out::println);
    }
}

三、Lambda 语法

语法:() -> {} ():Lambda的形参列表,也就是接口里面那个抽象方法的形参列表。 ->:Lambda的操作符,可以理解为参数和Lambda体的分隔符。 {}:实现了接口中的抽象方法的方法体。

我们还是以一个简单的例子,由浅到深学习 Lambda 语法。按照语法,我们可以写出如下 Lambda 表达式,(String name, int age) 是参数列表,-> 是分隔符,{} 中的代码是方法体。

// 函数接口
@FunctionalInterface
public interface Human {
    // 抽象方法,需要被实现
    String speak(String name, int age);
}
public class LambdaDemo {
    public static void main(String[] args) {
        Human human = (String name, int age) -> {
            System.out.println("My name is " + name + " ,I am " + age + " years old.");
            return name;
        };
        human.speak("Mr.nobody", 18);
    }
}

当然,() 括号内的参数类型还能省略(推荐)。

public class LambdaDemo {
    public static void main(String[] args) {
        Human human = (name, age) -> {
            System.out.println("My name is " + name + " ,I am " + age + " years old.");
            return name;
        };
        human.speak("Mr.nobody", 18);
    }
}

如果是只有一个参数,() 也能省略

@FunctionalInterface
public interface Human {
    // 抽象方法,需要被实现
    String speak(String name);
}
public class LambdaDemo {
    public static void main(String[] args) {
        Human human = name -> {
            System.out.println("My name is " + name + ".");
            return name;
        };
        human.speak("Mr.nobody");
    }
}

如果,方法体 {} 中,只有一行语句,{} 也能省略(推荐)。

@FunctionalInterface
public interface Human {
    // 抽象方法,需要被实现
    void speak(String name, int age);
}
public class LambdaDemo {
    public static void main(String[] args) {
        Human human = (name, age) -> System.out
                .println("My name is " + name + " ,I am " + age + " years old.");
        human.speak("Mr.nobody", 18);
    }
}

如果方法体需要返回值,而且只有一行语句,那 {} 大括号和 return 关键字都可以省略(推荐)。

@FunctionalInterface
public interface Human {
    // 抽象方法,需要被实现
    String speak(String name, int age);
}
public class LambdaDemo {
    public static void main(String[] args) {
        Human human = (name, age) -> "My name is " + name + " ,I am " + age + " years old.";
        human.speak("Mr.nobody", 18);
    }
}

天下网TXWEB镇江市阿里店铺运营宁波市seo排名西安市网站优化常德市网站搭建哪家专业绥化市网站制作多少钱广州市营销网站建设多少钱天水市网站设计报价衡阳市优化哪家专业朝阳市建站哪家好保山网页设计营口网站建设哪家好鹤壁市网页制作推荐萍乡市优化公司深圳市企业网站设计价格济南市网站制作报价株洲市seo排名价格玉林品牌网站设计咸宁市定制网站公司丽江市企业网站设计公司宿州网站设计多少钱遵义做网站报价聊城网站制作多少钱吴忠做网站南阳市网站搭建哪家好泰州网络推广推荐娄底网站开发价格白城做网站孝感市建站价格三明市网站开发推荐岳阳市企业网站改版报价香港通过《维护国家安全条例》两大学生合买彩票中奖一人不认账让美丽中国“从细节出发”19岁小伙救下5人后溺亡 多方发声卫健委通报少年有偿捐血浆16次猝死汪小菲曝离婚始末何赛飞追着代拍打雅江山火三名扑火人员牺牲系谣言男子被猫抓伤后确诊“猫抓病”周杰伦一审败诉网易中国拥有亿元资产的家庭达13.3万户315晚会后胖东来又人满为患了高校汽车撞人致3死16伤 司机系学生张家界的山上“长”满了韩国人?张立群任西安交通大学校长手机成瘾是影响睡眠质量重要因素网友洛杉矶偶遇贾玲“重生之我在北大当嫡校长”单亲妈妈陷入热恋 14岁儿子报警倪萍分享减重40斤方法杨倩无缘巴黎奥运考生莫言也上北大硕士复试名单了许家印被限制高消费奥巴马现身唐宁街 黑色着装引猜测专访95后高颜值猪保姆男孩8年未见母亲被告知被遗忘七年后宇文玥被薅头发捞上岸郑州一火锅店爆改成麻辣烫店西双版纳热带植物园回应蜉蝣大爆发沉迷短剧的人就像掉进了杀猪盘当地回应沈阳致3死车祸车主疑毒驾开除党籍5年后 原水城县长再被查凯特王妃现身!外出购物视频曝光初中生遭15人围殴自卫刺伤3人判无罪事业单位女子向同事水杯投不明物质男子被流浪猫绊倒 投喂者赔24万外国人感慨凌晨的中国很安全路边卖淀粉肠阿姨主动出示声明书胖东来员工每周单休无小长假王树国卸任西安交大校长 师生送别小米汽车超级工厂正式揭幕黑马情侣提车了妈妈回应孩子在校撞护栏坠楼校方回应护栏损坏小学生课间坠楼房客欠租失踪 房东直发愁专家建议不必谈骨泥色变老人退休金被冒领16年 金额超20万西藏招商引资投资者子女可当地高考特朗普无法缴纳4.54亿美元罚金浙江一高校内汽车冲撞行人 多人受伤

天下网TXWEB XML地图 TXT地图 虚拟主机 SEO 网站制作 网站优化