Lambda

Lambda

Lambda是一个匿名函数,我们可以把Lambda表达式理解为是一段可以传递的代码(将代码像数据一样传递)。可以写出更简洁、更灵活的代码。作为一种更紧凑的代码风格,使Java的语言表达能力得到提升

体验

/**
 * lambda 初体验
 */
public class lambdaTest {
    @Test
    public void test() {
        // 匿名内部类
        Comparator<Integer> comparator = new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return Integer.compare(o1, o2);
            }
        };

        TreeSet<Integer> treeSet = new TreeSet<>(comparator);

        // lambda
        Comparator<Integer> comparator1 = (x, y) -> Integer.compare(x, y);

        TreeSet<Integer> treeSet1 = new TreeSet<>(comparator1);
    }
}

演变

原始做法

有这么一个需求,求一个班级中年龄大于16岁的同学;

public class Student {

    private String name;

    private Integer age;

    private Integer scope;

    public Student() {
    }

    public Student(String name, Integer age, Integer scope) {
        this.name = name;
        this.age = age;
        this.scope = scope;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public Integer getScope() {
        return scope;
    }

    public void setScope(Integer scope) {
        this.scope = scope;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", scope=" + scope +
                '}';
    }
}
        List<Student> students = Arrays.asList(
            new Student("张三", 18, 90),
            new Student("李四", 14, 95),
            new Student("王五", 16, 98),
            new Student("牛二", 18, 95),
            new Student("赵六", 15, 96)
    );

    // 年龄大于16
    @Test
    public void test01() {
        List<Student> stus = new ArrayList<>();

        for (Student student : students) {
            if (student.getAge() > 16) {
                stus.add(student);
            }
        }

        for (Student student : stus) {
            System.out.println(student);
        }
    }

需求变更:求班级中分数大于95的同学

        // 分数大于95
    @Test
    public void test02() {
        List<Student> stus = new ArrayList<>();

        for (Student student : students) {
            if (student.getScope() > 95) {
                stus.add(student);
            }
        }

        for (Student student : stus) {
            System.out.println(student);
        }
    }

可以看出,有效代码只有if判断中的条件,这样情况下,随着需求的变更,我们会书写很多重复性代码

优化方式一:策略设计模式

需求:年龄大于16

  // 定义过滤接口
  public interface MyPredicate<T> {
      public boolean test(T t);
  }
  // 年龄过滤
  public class AgePredicate implements MyPredicate<Student> {
      @Override
      public boolean test(Student student) {
          return student.getAge() > 16;
      }
  }
      public List<Student> filterStu(List<Student> students, MyPredicate myPredicate) {
        List<Student> list = new ArrayList<>();
        for (Student student : students) {
            if (myPredicate.test(student)) {
                list.add(student);
            }
        }
        return list;
    }

    // 年龄大于16
    @Test
    public void test03() {
        List<Student> students = filterStu(this.students, new AgePredicate());
        for (Student student : students) {
            System.out.println(student);
        }
    }

需求变更:分数大于95

// 分数过滤
public class ScopePredicate implements MyPredicate<Student> {
    @Override
    public boolean test(Student student) {
        return student.getScope() > 95;
    }
}
      public List<Student> filterStu(List<Student> students, MyPredicate myPredicate) {
        List<Student> list = new ArrayList<>();
        for (Student student : students) {
            if (myPredicate.test(student)) {
                list.add(student);
            }
        }
        return list;
    }

        // 分数大于95
    @Test
    public void test04() {
        List<Student> students = filterStu(this.students, new ScopePredicate());
        for (Student student : students) {
            System.out.println(student);
        }
    }

此时,根据需求的不同,我们只需要定义MyPredicate的实现类来编写过滤条件即可,不要修改原有的逻辑代码

优化方式二:匿名内部类

需求:年龄大于16

        // 年龄大于16
    @Test
    public void test05() {
        List<Student> students = filterStu(this.students, new MyPredicate<Student>() {
            @Override
            public boolean test(Student stu) {
                return stu.getAge() > 16;
            }
        });
        for (Student student : students) {
            System.out.println(student);
        }
    }

需求变更:分数大于95

    // 分数大于95
    @Test
    public void test06() {
        List<Student> students = filterStu(this.students, new MyPredicate<Student>() {
            @Override
            public boolean test(Student stu) {
                return stu.getScope() > 95;
            }
        });
        for (Student student : students) {
            System.out.println(student);
        }
    }

通过匿名内部类的方式实现过滤代码,可以避免为每一个需求定一个实现类来编写过滤代码;但是此时的代码还是略显臃肿

优化方式三:lambda表达式

需求:年龄大于16

        @Test
    public void test07() {
        List<Student> students = filterStu(this.students, x -> x.getAge() > 16);
        students.forEach(System.out::println);
    }

需求变更:分数大于95

    @Test
    public void test08() {
        List<Student> students = filterStu(this.students, x -> x.getScope() > 95);
        students.forEach(System.out::println);
    }

此时,我们可以看出,有效代码只有x -> x.getAge() > 16,x -> x.getScope() > 95,极大提高代码的简洁性和可读性

语法

Java8中引入了一个新的操作符->,该操作符称为箭头操作符或Lambda操作符,->将Lambda表达式拆分成左右两部分

左侧:Lambda表达式的参数列表(对应匿名函数,重写方法的参数列表)

右侧:Lambda表达式中所执行的功能,即Lambda体(对应匿名函数,重写方法的方法体)

语法格式一:无参数,无返回值

() -> System.out.println("Hello lambda");
    @Test
    public void test01() {
        Runnable r = new Runnable() {
            @Override
            public void run() {
                System.out.println("Hello world");
            }
        };
        r.run();
        System.out.println("---------------------");

        Runnable r1 = () -> System.out.println("Hello lambda");
        r1.run();
    }

语法格式二:有一个参数,无返回值

(x) -> System.out.println(x);
// 若只有一个参数,小括号可以不写
x -> System.out.println(x);

语法格式三:多个参数,有返回值,多条执行语句

@Test
    public void test02() {
        // 匿名函数方式
        Comparator<Integer> comparator = new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return Integer.compare(o1, o2);
            }
        };
        System.out.println("-------------------------------");
                
        // 多条执行语句
        Comparator<Integer> comparator1 = (o1, o2) -> {
            System.out.println(o1 + "--------" + o2);
            return Integer.compare(o1, o2);
        };
        System.out.println("-------------------------------");

        // 若只有一条执行语句,return和大括号可以省略不写
        Comparator<Integer> comparator2 = (o1, o2) -> Integer.compare(o1, o2);
        
    }

函数式接口

接口中只有一个抽象方法的接口,称为函数式接口,可以使用注解@FunctionalInterface修饰;Lambda表达式需要“函数式接口”的支持

内置四大核心函数式接口

接口 接口说明 参数类型 返回类型 用途
Consumer<T> 消费型接口 T void 对类型为T的对象操作,方法:void accept(T t);
Supplier<T> 供给型接口 T 返回T类型对象,方法:T get();
Function<T,R> 函数型接口 T R 对T类型对象操作,并返回R类型结果;方法:R apply(T t);
Predicate<T> 断言型接口 T boolean 确定T类型对象是否满足某中约束,并返回boolean值;方法:test(T t);

扩展接口

接口 参数类型 返回类型 用途
BiFunction<T, U, R> T, U R 对类型为T, U参数应用操作,返回R类型的结果;方法 R apply(T t, U u);
UnaryOpetator<T> (Function子接口) T T 对类型为T的对象进行一元运算,并返回T类型的结果;方法:T apply(T t);
BinaryOperator<T> (BiFunction子接口) T, T T 对类型为T的对象进行二元运算,并返回T类型的结果;方法:T apply(T t1, T t2);
BiConsumer(T, U) T, U void 对类型为T, U参数应用操作;方法:void accept(T t, U u)
ToIntFunction<T><br />ToLongFunction<T><br />ToDoubleFunction<T> T int<br />long<br />double 分别计算int, long, double值的函数
IntFunction<R><br />LongFunction<R><br />DoubleFunction<R> int<br />long<br />double R 参数分别为int, long, double 类型的函数

方法引用

若Lambda体中的内容有方法已经实现,我们可以使用方法引用方法引用可以理解为是Lambda表达式的另外一种表现形式

对象::实例方法名

注意:Lambda 体中调用方法的参数列表和返回值类型,要与函数式接口中抽象方法的参数列表和返回值类型保持一致

    public void test01() {
        // 方式一
        Consumer<String> c1 = (x) -> System.out.println(x);

        // 方式二
        PrintStream out1 = System.out;
        Consumer<String> c2 = (x) -> out1.println(x);
        
        // 方式三
        PrintStream out2 = System.out;
        Consumer<String> c3 = out2::println;
        
        // 方式四
        Consumer<String> c4 = System.out::println;
    }

类名::静态方法名

注意:Lambda 体中调用方法的参数列表和返回值类型,要与函数式接口中抽象方法的参数列表和返回值类型保持一致

    public void test02() {
        // 方式一
        Comparator<Integer> comparator1 = (x, y) -> Integer.compare(x, y);
        
        // 方式二
        Comparator<Integer> comparator2 = Integer::compare;
    }

类名::实例方法名

注意:Lambda表达式参数列表中第一参数是实例方法的调用者,第二个参数是实例方法的参数时,可以使用ClassName::Method

    @Test
    public void test03() {
        // 方式一
        BiPredicate<String, String> predicate01 = (x, y) -> x.equals(y);
        
        // 方式二
        BiPredicate<String,String> predicate02 = String::equals;
    }

构造器引用

格式: ClassName::new

注意:Lambda体中需要调用构造器的参数列表要与函数式接口中抽象方法的参数列表保持一致

    @Test
    public void test04() {
        // 无参
        Supplier<Date> s1 = () -> new Date();
        Supplier<Date> s2 = Date::new;
        
        // 一个参数
        Function<Long, Date> s3 = (x) -> new Date(x);
        Function<Long, Date> s4 = Date::new;
    }

数组引用

格式:Type[] ::new

    @Test
    public void test05() {
        // 方式一
        Function<Integer, String[]> f1 = (x) -> new String[x];
        
        // 方式二
        Function<Integer, String[]> f2 = String[]::new;
    }
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,222评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,455评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,720评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,568评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,696评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,879评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,028评论 3 409
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,773评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,220评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,550评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,697评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,360评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,002评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,782评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,010评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,433评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,587评论 2 350