Lambda

Lambda

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

体验

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

        TreeSet treeSet = new TreeSet<>(comparator);

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

        TreeSet 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 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 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 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 {
      public boolean test(T t);
  }
  // 年龄过滤
  public class AgePredicate implements MyPredicate {
      @Override
      public boolean test(Student student) {
          return student.getAge() > 16;
      }
  }
      public List filterStu(List students, MyPredicate myPredicate) {
        List list = new ArrayList<>();
        for (Student student : students) {
            if (myPredicate.test(student)) {
                list.add(student);
            }
        }
        return list;
    }

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

需求变更:分数大于95

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

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

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

优化方式二:匿名内部类

需求:年龄大于16

        // 年龄大于16
    @Test
    public void test05() {
        List students = filterStu(this.students, new MyPredicate() {
            @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 students = filterStu(this.students, new MyPredicate() {
            @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 students = filterStu(this.students, x -> x.getAge() > 16);
        students.forEach(System.out::println);
    }

需求变更:分数大于95

    @Test
    public void test08() {
        List 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 comparator = new Comparator() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return Integer.compare(o1, o2);
            }
        };
        System.out.println("-------------------------------");
                
        // 多条执行语句
        Comparator comparator1 = (o1, o2) -> {
            System.out.println(o1 + "--------" + o2);
            return Integer.compare(o1, o2);
        };
        System.out.println("-------------------------------");

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

函数式接口

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

内置四大核心函数式接口

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

扩展接口

接口 参数类型 返回类型 用途
BiFunction T, U R 对类型为T, U参数应用操作,返回R类型的结果;方法 R apply(T t, U u);
UnaryOpetator (Function子接口) T T 对类型为T的对象进行一元运算,并返回T类型的结果;方法:T apply(T t);
BinaryOperator (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
ToLongFunction
ToDoubleFunction
T int
long
double
分别计算int, long, double值的函数
IntFunction
LongFunction
DoubleFunction
int
long
double
R 参数分别为int, long, double 类型的函数

方法引用

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

对象::实例方法名

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

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

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

类名::静态方法名

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

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

类名::实例方法名

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

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

构造器引用

格式: ClassName::new

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

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

数组引用

格式:Type[] ::new

    @Test
    public void test05() {
        // 方式一
        Function f1 = (x) -> new String[x];
        
        // 方式二
        Function f2 = String[]::new;
    }

你可能感兴趣的:(Lambda)