Java8新特性——Lambda

2014年3月发布
公司往往不是追求技术的新,而且追求技术的稳定。所以大多用的之前的版本。

Java 8 新特性简介

    速度更快
        修改底层数据结构:如HashMap(数组-链表-红黑树),HashSet,ConcurrentHashMap(CAS算法)
        修改垃圾回收机制:取消堆中的永久区(PremGen)->回收条件苛刻,使用元空间(MetaSpace)->直接使用物理内存->加载类文件
    代码更少(增加了新的语法Lambda表达式)
    强大的Stream API
    便于并行
    最大化减少空指针异常 Optional容器类
Java8新特性——Lambda_第1张图片

1 Lambda表达式

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

public class TestLambda {

    //原来的匿名内部类
    @Test
    public void test1(){
        Comparator com=new Comparator(){
            @Override
            public int compare(Integer o1, Integer o2) {
                return Integer.compare(o1, o2);
            }
        };
        TreeSet ts=new TreeSet<>(com);
    }

    //Lambda表达式
    @Test
    public void test2(){
        Comparator com=(x,y)->Integer.compare(x,y);
        TreeSet ts=new TreeSet<>(com);
    }

    List employees=Arrays.asList(
            new Employee("张三",18,9496.2),
            new Employee("李四",52,2396.2),
            new Employee("王五",56,996.2),
            new Employee("赵六",8,94.2)
    );

    @Test
    public void test3(){
        //需求:获取当前公司中员工年龄大于35的员工信息
        List emps=filterEmplyees1(employees);
        for(Employee e:emps){
            System.out.println(e);
        }
        System.out.println("---------------------");

        //需求:获取当前公司中员工工资大于2000的员工信息
        List emps2=filterEmplyees2(employees);
        for(Employee e:emps2){
            System.out.println(e);
        }
    }

    public List filterEmplyees1(List list){
        List emps=new ArrayList();
        for (Employee emp : list) {
            if(emp.getAge()>=35){
                emps.add(emp);
            }
        }
        return emps;
    }

    public List filterEmplyees2(List list){
        List emps=new ArrayList();
        for (Employee emp : list) {
            if(emp.getSalary()>=2000){
                emps.add(emp);
            }
        }
        return emps;
    }

    @Test
    public void test4(){
        List emps=filterEmplyees(employees,new FilterEmployeeByAge());
        for(Employee e:emps){
            System.out.println(e);
        }
        System.out.println("---------------------");
        List emps2=filterEmplyees(employees,new FilterEmployeeBySalary());
        for(Employee e:emps2){
            System.out.println(e);
        }
    }

    //优化方式一:策略设计模式
    public List filterEmplyees(List list,MyPredicate myPredicate){
        List emps=new ArrayList();
        for (Employee emp : list) {
            if(myPredicate.test(emp)){
                emps.add(emp);
            }
        }
        return emps;
    }

    //优化方式二:匿名内部类
    @Test
    public void test5(){
        List list=filterEmplyees(employees, new MyPredicate() {
            @Override
            public boolean test(Employee t) {
                return t.getSalary()>=2000;
            }
        });

        for (Employee employee : list) {
            System.out.println(employee);
        }
    }

    //优化方式三:Lambda表达式
    @Test
    public void test6(){
        List list=filterEmplyees(employees, (e)->e.getSalary()>=2000);
        list.forEach(System.out::println);
    }

    //优化方式四:stream API
    @Test
    public void test7(){
        employees.stream()
                 .filter((e)->e.getSalary()>=2000)
                 .forEach(System.out::println);

        System.out.println("------------------");

        employees.stream()
                 .map(Employee::getName)
                 .forEach(System.out::println);
    }
}

MyPredicate.java

public interface MyPredicate {
    public boolean test(T t);
}

FilterEmployeeBySalary.java

public class FilterEmployeeBySalary implements MyPredicate{
    @Override
    public boolean test(Employee t) {
        return t.getSalary()>=2000;
    }
}

1.1 Lambda表达式的基础语法

Java8中引入了一个新的操作符”->” 该操作符称为箭头操作符或Lambda操作符,箭头操作符将Lambda表达式拆分成两部分:
左侧:Lambda 表达式的参数列表
右侧:Lambda 表达式中所需执行的功能,即 Lambda 体

语法格式一:无参数,无返回值 
()->System.out.println(“Hello Lambda!”);

    @Test
    public void test1(){
        //通过匿名内部类的方式实现接口
        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);

    @Test
    public void test2(){
        Consumer con=(x)->System.out.println(x);//对Consumer接口中有一个参数的accept方法的实现
        con.accept("啦啦啦");
    }

语法格式三:若只有一个参数,小括号可以不写
 

x->System.out.println(x);

语法格式四:有两个以上的参数,有返回值,并且Lambda体中有多条语句

    @Test
    public void test3(){
        Comparator com=(x,y)->{
            System.out.println("函数式接口");
            return Integer.compare(x, y);
        };
    }

语法格式五:若Lambda体中只有一条语句,大括号和 return 都可以省略不写

 @Test
    public void test4(){
        Comparator com=(x,y)->Integer.compare(x, y);
    }

语法格式六:Lambda表达式的参数列表的数据类型可以省略不写,因为JVM编译器通过上下文推断出,数据类型,即“类型推断”

(Integer x,Integer y)->Integer.compare(x,y);

1.2 Lambda表达式需要“函数式接口”的支持

只包含一个抽象方法的接口,称为 函数式接口。

你可以通过 Lambda 表达式来创建该接口的对象。(若 Lambda表达式抛出一个受检异常,那么该异常需要在目标接口的抽象方法上进行声明)。

我们可以在任意函数式接口上使用 @FunctionalInterface 注解,这样做可以检查它是否是一个函数式接口,同时 javadoc 也会包含一条声明,说明这个接口是一个函数式接口。

作为参数传递 Lambda 表达式:为了将 Lambda 表达式作为参数传递,接收 Lambda 表达式的参数类型必须是与该 Lambda 表达式兼容的函数式接口的类型。
MyFun.java

@FunctionalInterface
public interface MyFun {
    public Integer getValue(Integer num);
}
    //需求:对一个数进行运算
    @Test
    public void  test6(){
        Integer num=operation(100, (x)->x*x);
        System.out.println(num);

        System.out.println(operation(200, (y)->y+200));
    }

    public Integer operation(Integer num,MyFun mf){
        return mf.getValue(num);
    }

1.3 Java提供的内置函数式接口

为了免去用户每次使用Lambda表达式时,都自行创建函数式接口,Java提供了4大核心内置 函数式接口

Java8新特性——Lambda_第2张图片

/**
 * 
 * java内置四大函数式接口
 * 
 * Consumer :消费型接口
 *          void accept(T t);
 * 
 * Supplier :供给型接口
 *          T get();
 * 
 * Function :函数型接口
 *          R apply(T t);
 * 
 * Predicate :断言型接口
 *          boolean test(T t);
 *
 */
public class TestLambda3 {

    //Consumer 消费型接口:
    @Test
    public void test1(){
        happy(1000,(m) ->System.out.println("喜欢大宝剑,消费:"+m+"元"));
    }
    public void happy(double money,Consumer con){
        con.accept(money);
    }

    //Supplier 供给型接口:
    //需求:产生指定个数的整数,并放入集合中
    @Test
    public void test2(){
        List numList=getNumList(10, ()->(int)(Math.random()*100));
        for (Integer num : numList) {
            System.out.println(num);
        }
    }
    public List getNumList(int num,Supplier sup){
        List list=new ArrayList<>();
        for (int i = 0; i < num; i++) {
            Integer n=sup.get();
            list.add(n);
        }
        return list;
    }

    //Function 函数型接口:
    @Test
    public void test3(){
        String newStr=strHandler("\t\t\t 啦啦啦德玛西亚  ", (str)->str.trim());
        System.out.println(newStr);

        String subStr=strHandler("无与伦比,为杰沉沦", (str)->str.substring(5,9));
        System.out.println(subStr);
    }
    //需求:处理字符串
    public String strHandler(String str,Function fun){
        return fun.apply(str);
    }

    //Predicate 断言型接口:
    @Test
    public void test4(){
        List list=Arrays.asList("Hello","jj","Lambda","www","ok");
        List strList=filterStr(list, (s)->s.length()>3);
        for (String string : strList) {
            System.out.println(string);
        }
    }
    //需求:将满足条件的字符串,放入集合中
    public List filterStr(List list,Predicate pre){
        List strList=new ArrayList<>();
        for ( String str : list) {
            if(pre.test(str)){
                strList.add(str);
            }
        }
        return strList;
    }
}

 

Java8新特性——Lambda_第3张图片

1.4 方法引用与构造器引用

/*
 * 一、方法引用:若Lambda体中的内容有方法已经实现了,我们可以使用“方法引用”(可以理解为方法引用时Lambda表达式的另一种表现形式)
 * 
 * 主要有三种语法格式:
 * 对象::实例方法名
 * 类::静态方法名
 * 类::实例方法名
 * 
 * 注意:
 * 1、Lambda体中调用方法的参数列表与返回值类型,要与函数式接口中抽象方法的函数列表和返回值类型保持一致!
 * 2、若Lambda参数列表中的第一个参数是 实例方法的调用者,而第二个参数是实例方法的参数时,可以使用ClassName::method
 * 
 * 
 * 二、构造器引用:
 * 格式:
 * ClassName::new
 * 
 * 注意:需要调用的构造器的参数列表要与函数式接口中抽象方法的参数列表保持一致!
 * 
 * 
 * 三:数组引用:
 *  Type::new;
 *  
 */
public class TestMethodRef { 

    //对象::实例方法名
    @Test
    public void test1(){
        PrintStream ps1=System.out;
        Consumer con=(x)->ps1.println(x);//生成了一个实现了Consumer接口的类的对象

        PrintStream ps=System.out;
        Consumer con1=ps::println;//相当于上面,引用了ps对象的println()方法

        Consumer con2=System.out::println;
        con2.accept("abcdef");
    }

    @Test
    public void test2(){
        final Employee emp=new Employee();
        Supplier sup=()->emp.getName();//代替匿名内部类
        String str=sup.get();
        System.out.println(str);

        Supplier sup2=emp::getAge;
        Integer num=sup2.get();
        System.out.println(num);
    }

    //类::静态方法名
    @Test
    public void test3(){
        Comparator com=(x,y)->Integer.compare(x,y);
        Comparator com1=Integer::compare;
    }

    //类::实例方法名
    @Test
    public void test4(){
        BiPredicate bp=(x,y)->x.equals(y);
        BiPredicate bp2=String::equals;
    }


    //构造器引用
    @Test
    public void test5(){
        Supplier sup=()->new Employee();

        //构造器引用方式
        Supplier sup2=Employee::new;//使用无参构造器
        Employee emp=sup2.get();
        System.out.println(emp);

        Function fun2=(x)->new Employee(x);
        Employee emp2=fun2.apply(101);
        System.out.println(emp2);

        BiFunction bf=Employee::new;
    }

    //数组引用
    @Test
    public void test6(){
        Function fun=(x)->new String[x];
        String[] strs=fun.apply(10);
        System.out.println(strs.length);

        Function fun2=String[]::new;
        String[] str2=fun2.apply(20);
        System.out.println(str2.length);
    }
}

 

你可能感兴趣的:(java)