首页 > 技术文章 > Java--8--新特性--Lambda

wzqjy 2017-11-27 12:23 原文

java9 都出来了,我才开始接触到java8的新特性,有点脱节啊。。

Lambda是一个匿名函数,可以理解为一段可以传递的代码,将代码像数据一样传递,下面是一个小例子。

public class Employee
{
    private String name;
    private int age;
    private int salary;
//getter and setter
}
//-----------------------------------------------
public interface MyInter {
     Boolean filter(Employee list);
}
//-----------------------------------------------
public class MyInterImpl implements  MyInter {
    @Override
    public Boolean filter(Employee list) {
        return list.getage()>35;
    }
}
//-----------------------------------------------
package LambdaP;

import org.junit.Test;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class LamBdaDemo {

    public static final List<Employee> employees = Arrays.asList(
            new Employee("张三", 19, 2000),
            new Employee("张四", 33, 3000),
            new Employee("张五", 38, 4000),
            new Employee("张六", 41, 2500),
            new Employee("张七", 42, 5500)
    );

    //需求:当前公司中员工的年冷大于三十五的员工信息
    //传统方法
    public List<Employee> getEmployees() {
        List<Employee> list = new ArrayList<>();
        for (Employee employee : employees) {
            if (employee.getage() > 35) {
                list.add(employee);
            }
        }
        return list;
    }

    public void test1() {
        List<Employee> employees = this.getEmployees();
        for (Employee e : employees) {
            System.out.println(e);
        }
    }

    //方法2
    public  List<Employee> filterEmployee(List<Employee> list,MyInter inter){
        List<Employee> newlist = new ArrayList<>();
        for (Employee employee : list) {
            if(employee.getage()>35){
                newlist.add(employee);
            }
        }
        return newlist;
    }
    public  void test2(){
        List<Employee> employees = this.filterEmployee(LamBdaDemo.employees, new MyInterImpl());
        for (Employee employee : employees) {
            System.out.println(employee);
        }
    }
    //Lambda
    @Test
    public void test3(){
        List<Employee> employees = this.filterEmployee(LamBdaDemo.employees, (e) -> e.getage() > 35);
        for (Employee employee : employees) {
            System.out.println(employee);
        }
        System.out.println("Lambda .....");
    }
    //方法三:如果以上的都不存在的话   Stream API  也是在java8中新更新的
    @Test
    public void test4(){
        employees.stream().filter((e)->e.getage()>35).forEach(System.out::println);
    }
}

以上就能看出来Lambda的便捷性,下面开始我自己的学习笔记

import org.junit.Test;
import java.util.Comparator;
import java.util.function.Consumer;

public class LamBdaDemo2 {
    /**
     * Lambda的基本语法:java8中新引入了“->”操作符,简称箭头操作符或者Lambda操作符,
     *                  他将Lambda表达式拆分成两部分
     *                          1. 左侧:对应Lambda的参数列表
     *                          2. 右侧:Lambda所要执行的功能,”Lambda体“
     *                  箭头操作符的左侧对应接口中的参数列表,右侧即所要实现的方法。
     *         JDK 1.7 之前内部类引用同级的外部变量必须加final,现在不用手动加,默认,但是还是在内部类中不能操作外部变量,Lambda中同样适用。
     * 语法格式一:无参数,无返回值   test1
     *           ()-> System.out.println("Hello");
     * 语法格式二:有一个参数,无返回值 test2
     *           (e)-> { 实现 };
     * 语法格式三:有两个以上个参数,并且Lambda体有多条语句,有返回值。 test3
     *           (x,y)->{ 实现 };
     * 语法格式四:Lambda 表达式的参数列表类型可以省略不写,如果写就必须全部写,JVM可以自己推断出参数类型。
     *             Comparator<Integer> comparator2 = (Integer x,Integer y) ->   Integer.compare(x,y);
     *
     *          左右遇一括号省    :即Lambda体重左右两边如果只有一个参数或者一条语句的话括号可以省略不写。
     *          左侧推断类型省    :JVM自己推断出来
     *
     * Lambda表达式需要“函数式接口的支持”
     *          函数式接口:若接口中只有一个抽象方法的接口叫做函数式接口
     *          用Ctrl加左击点击接口名可以查看系统提供的接口是否为函数式接口
     *          系统提供的函数式接口都加有  @FunctionalInterface  注解
     *          此注解可以检查是否是函数式接口
     */


    public  void test1(){
        Runnable runnable1 = new Runnable() {
            @Override
            public void run() {
                System.out.println("Hello");
            }
        };
        runnable1.run();
        System.out.println("*********************");
        Runnable runnable = ()-> System.out.println("Hello Lambda");
        runnable.run();
    }
    public  void test2(){
        //consumer 定义  @FunctionalInterface
        //                public interface Consumer<T> {

        //Consumer 中的方法  void accept(T t);
        Consumer<String> consumer = (e) -> System.out.println("Lambda " + e);
        consumer.accept("Hello");

        //在只有一个参数的情况下小括号是可以省略不写的
        Consumer<String> con = e -> System.out.println("Lambda " + e);
        con.accept("Hello");
    }
    public  void test3(){
        Comparator<Integer> comparator = (x,y) ->{
            System.out.println("比较x y");
            return Integer.compare(x,y);
        };
        System.out.println(comparator.compare(1,2));

        System.out.println("**************************");
        //如果如上只有一条语句后面的大括号  可以省略不写
        Comparator<Integer> comparator2 = (x,y) ->   Integer.compare(x,y);
        int compare = comparator2.compare(2, 1);
        System.out.println(compare);
    }
}

下面来实现一个小功能:对两个数的任意操作

@FunctionalInterface
public interface MyInter2 {
     Integer getValue(Integer x);
}

 

执行结果为:16.

需求:按年龄排序,年龄相同按工资排序

package LambdaP;

import org.junit.Test;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

public class LamBdaDemo3 {
    public static final List<Employee> employees = Arrays.asList(
            new Employee("张三", 19, 2000),
            new Employee("张四", 33, 3000),
            new Employee("张五", 38, 4000),
            new Employee("张七", 42, 5500),
            new Employee("张六", 42, 2500)
    );

    //需求:先按年龄排序,年龄相同按工资排序
    
    public void test() {
        //此方法是排序方法,定制排序
        Collections.sort(employees, new Comparator<Employee>() {
            @Override
            public int compare(Employee e1, Employee e2) {
                if (e1.getage() == e2.getage()) {
                    return Integer.compare(e1.getSalary(), e2.getSalary());
                } else {
                    return Integer.compare(e1.getage(), e2.getage());
                }
            }
        });
        for (int i = 0; i < employees.size(); i++) {
            System.out.println(employees.get(i));
        }
    }
    
    //Lambda
    @Test
    public void test1(){
        Collections.sort(employees,(e1,e2)->{
            if (e1.getage() == e2.getage()) {
                return Integer.compare(e1.getSalary(), e2.getSalary());
            } else {
                return Integer.compare(e1.getage(), e2.getage());
            }
        });
        System.out.println("Lambda");
        for (int i = 0; i < employees.size(); i++) {
            System.out.println(employees.get(i));
        }
    }
}

 

下面来看一下Lambda表达式的四大函数式接口!

package LambdaP;
import org.junit.Test;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
public class LamBdaDemo4 {
    /**
     * Java 8 内置的四大函数式接口
     * 1. Consumer<T> :消费型接口  , 即只传入不返回。
     * void accept(T t);
     * 2. Supplier<T> : 供给型接口  , 即只返回。
     * T get();
     * 3. Function<T,R> : 函数式接口,T传入参数类型,R返回参数类型
     * R apply(T t);
     * 4. Predicate : 断言型接口,判断
     * boolean test(T t);
     */
    public void test1() {
        //Consumer<T> 消费型接口:有去无回
        this.toHappy(1000d, (m) -> System.out.println("学好Java去花:" + m + "年吧!"));
    }
    public void toHappy(Double money, Consumer<Double> consumer) {
        consumer.accept(money);
    }
    //Supplier<T> 供给型接口
    public void test2() {
        List<String> string = this.getString(3, () -> "X");
        System.out.println(string);
    }
    public List<String> getString(Integer num, Supplier<String> su) {
        List<String> list = new ArrayList<>();
        for (Integer i = 0; i < num; i++) {
            list.add(su.get());
        }
        return list;
    }
    //函数式接口 Function<T,R>
    public void test3() {
        Integer fun = this.getFun("3", (e) -> Integer.parseInt(e));
        System.out.println(fun);

    }
    public Integer getFun(String string, Function<String, Integer> function) {
        return function.apply(string);
    }
    //断言型接口 Predicate<T>
    @Test
    public void test4() {
        String[] strings = new String[]{"A", "B", "C", "DDD", "TT"};
        String[] str = this.getStr(strings, (x) -> x.length() > 1);
        for (int i = 0; i < str.length; i++) {
            if (str[i] != null)
                System.out.println(str[i]);
        }
    }
    public String[] getStr(String[] strings, Predicate<String> predicate) {
        String[] str = new String[strings.length];
        int x = 0;
        for (int i = 0; i < strings.length; i++) {
            if (predicate.test(strings[i])) {
                str[x] = strings[i];
                x++;
            }
        }
        return str;
    }
}

 以及Lambda中的方法调用

package LambdaP;
import org.junit.Test;
import java.io.PrintStream;
import java.util.Comparator;
import java.util.function.BiPredicate;
import java.util.function.Consumer;
import java.util.function.Supplier;
public class LamBdaDemo5 {
  //方法引用 与 构造器引用
    /**
     * 方法引用 : 若Lambda体中的内容有方法已经实现了,那么我们可以使用“方法引用”
     *              是Lambda的另一种表现形式。
     *
     * 语法格式一 : 对象::实例方法名
     * 语法格式二 : 类::静态方法名
     * 语法格式三 : 类::实例方法名
     *
     */

    //对象::实例方法名
    public void test(){
        Consumer<String> consumer = (x) -> System.out.println(x);
        consumer.accept("Hello ");

        PrintStream out = System.out;
        Consumer<String> consumer1 = (x) -> out.println(x);
        consumer1.accept("Hello 2");

        Consumer<String> consumer2 = out::println;
        consumer2.accept("Hello 3");

        Consumer<String> consumer3 = System.out::println;
        consumer3.accept("Hello 4");
        /**
         * output:Hello
                        Hello 2
                        Hello 3
                        Hello 4
         */
        /**
         * 这种方式的调用解释如下: 例如Consumer是一个消费型接口,那么他就只需要参数而不需要返回值
         *                      Consumer定义如下
         *                          void accept(T t);
         *                      println定义如下
         *                           public void println(String x) {
                                        synchronized (this) {
                                        print(x);
                                        newLine();
                                    }
                                }
                            当然println方法可以输出任何类型的值,这里只是拿String做例子,如果想更改完全可以更改泛型即可
                            如上两个方法的定义,可以看出来println方法也是需要一个参数并且无返回值,那么与此消费型接口的参数以及
                            返回值是一致的,所以就可以省略参数以及后面的小括号不写,就能使用方法调用了,可以看成是println方法是
                            这个接口的一个实现。如下test2也是此方法调用的演示。
                总结:使用方法调用时,函数式接口中的抽象方法要与调用的方法的参数与返回值保持一致!!
         *
         */
    }
    public void  test2(){
        //供给型接口 无参有返回值
        Employee employee = new Employee("张三",18,999);
        Supplier<String> stringSupplier = ()-> employee.getName();
        String name = stringSupplier.get();
        System.out.println(name);//张三

        Supplier<Integer> integerSupplier = employee::getage;
        Integer integer = integerSupplier.get();
        System.out.println(integer);//18
    }
    public void test3(){
        //类::静态方法
        Comparator<Integer> comparator = (x,y) -> Integer.compare(x,y);
        int compare = comparator.compare(1, 2);
        System.out.println(compare);//-1

        Comparator<Integer> comparator1 = Integer::compare;
        int compare1 = comparator1.compare(1, 2);
        System.out.println(compare1);//-1
        //与上面总结的要求保持一致即可!!!
    }
    @Test
    public void test4(){
        //类::实例方法
        //需求:比较两个字符串是否一致?比较返回boolean,那么就需要使用到了断言型接口!!
        //但是Predicate接口中方法为 boolean test(T t);,这时候我们是需要两个参数的,那么他就不符合要求了,但是他的子类是可以使用的
        // BiPredicate :  boolean test(T t, U u);
        BiPredicate<String,String> biPredicate = (x,y) -> x.equals(y);
        boolean test = biPredicate.test("x", "X");
        System.out.println(test);//false

        BiPredicate<String,String> biPredicate1 = String::equals;
        boolean test1 = biPredicate1.test("C", "C");
        System.out.println(test1);
        /**
         * 这时候我们就有疑问了,为啥String不是对象也能去调用实例方法?
         * 这里需要遵循一个规则就可以使用此方法调用了!
         *      当接口中的一个参数是方法的调用者,另一个参数是调用方法的参数的时候,那么我们就可以使用(类::实例方法名了)
         */
    }
}

构造引用

package LambdaP;

import org.junit.Test;

import java.util.function.Function;
import java.util.function.Supplier;

public class LamBdaDemo6 {
    //方法引用 与 构造器引用

    /**
     * 构造器引用:
     * 格式: 类名::new
     */
    @Test
    public void test() {
        Supplier<Employee> supplier = () -> new Employee();
        Employee employee = supplier.get();
        System.out.println(employee);//LambdaP.Employee{name='null', age=0, salary=0}

        Supplier<Employee> supplier1 = Employee::new;
        Employee employee1 = supplier1.get();
        System.out.println(employee1);//LambdaP.Employee{name='null', age=0, salary=0}
        //上面是引用的是无参数的构造器,那么如果我们要引用有参数的构造器怎么办?
        //遵循规则,函数式接口中的参数列表代表了调用目标对象的哪个构造器,
        //比如调用一个参数的构造器

        Function<String, Employee> function = (x) -> new Employee(x);
        Employee zhangsan = function.apply("ZHANGSAN");
        System.out.println(zhangsan);//LambdaP.Employee{name='ZHANGSAN', age=0, salary=0}

        Function<String, Employee> function1 = Employee::new;
        Employee li = function.apply("李四");
        System.out.println(li);//LambdaP.Employee{name='李四', age=0, salary=0}
    }
}

数组引用

package LambdaP;

import org.junit.Test;

import java.util.function.Function;

public class LamBdaDemo7 {
    /**
     * 数组引用
     * Type::new
     */
    @Test
    public void test(){
        Function<Integer,String[]> function = (x) -> new String[x];
        String[] apply = function.apply(2);
        System.out.println(apply+"  " +apply.length);//[Ljava.lang.String;@1a28541  2

        Function<Integer,Integer[]> function1 = Integer[]::new;
        Integer[] apply1 = function1.apply(3);
        System.out.println(apply1+" " +apply1.length);//[Ljava.lang.Integer;@182830e 3
    }
}

 

推荐阅读