zoukankan      html  css  js  c++  java
  • Lambda表达式

    为什么使用Lambda表达式:

    Lambda是一个匿名函数,我们可以把Lambda表达式理解为是一段可以传递的代码(将代码像数据一样进行传递)。它是 JDK8 的一个新特性,可以取代大部分的匿名内部类,写出更优雅的 Java 代码,极大地优化代码结构。

    JDK 也提供了大量的内置函数式接口供我们使用,使得 Lambda 表达式的运用更加方便、高效。

    Lambda表达式对接口的要求:

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

    函数式接口:接口中只有一个抽象方法的接口,称为函数式接口。一般地,函数式接口都使用注解 @FunctionalInterface 修饰,来检查是否是函数式接口。

    Lambda表达式的基础语法:

    JAVA8 中引入了一个新的操作符  “->” ,该操作符称为 “箭头操作符”“Lambda操作符”

    箭头操作符将 Lambda 表达式拆分成两部分:

    左侧:Lambda 表达式的参数列表

    右侧:Lambda 表达式中所需要执行的功能,即 Lambda 体

    示例:

    import org.junit.Test;
    import java.util.Comparator;
    import java.util.function.Consumer;
    /**
     * Lambda 表达式测试
     */
    public class TestLambda1 {
    
        /**
         * 语法格式一: 无参数,无返回值
         */
        @Test
        public void test1(){
            int num = 0;//jdk 1.7 前,必须是 final,jdk 1.8 以后,无需手动加 final ,默认就是final 的
            Runnable r = new Runnable() {
                @Override
                public void run() {
                    System.out.println("Hello World!" + num);
                }
            };
            r.run();
            System.out.println("-------------------------------");
            Runnable r1 = () -> System.out.println("Hello Lambda !! " + num);
            r1.run();
        }
    
        /**
         * 语法格式二:有一个参数,并且无返回值
         */
        @Test
        public void test2(){
            //
            /*
                //用一个Java的工具类作为案例。
                @FunctionalInterface //函数式接口的注解
                public interface Consumer<T> {
                    void accept(T t);
                }
            */
            Consumer<String> con = new Consumer<String>() {
                @Override
                public void accept(String s) {
                    System.out.println("匿名内部类方式输入的参数:"+s);
                }
            };
            con.accept("Java 是最好的编程语言!");
            System.out.println("-------------------------------");
            Consumer<String> con1 = (s) -> System.out.println("Lambda 方式的输入参数:"+s);
            con1.accept("Java 是最好的编程语言!");
    
        }
        /**
         * 语法格式三:若只有一个参数,小括号可以省略不写
         */
        @Test
        public void test3(){
            Consumer<String> con1 = s -> System.out.println("Lambda 方式的输入参数:"+s);
            con1.accept("Java 是最好的编程语言!");
    
        }
        /**
         * 语法格式四:有两个以上的参数,有返回值,并且 Lambda 体中有多条语句,则可以加上 {}
         */
        @Test
        public void test4(){
            Comparator<Integer> com = (x,y) -> {
                System.out.println("Java 是最好的编程语言!");
                return Integer.compare(x, y);
            };
            System.out.println(com.compare(3,4));
        }
        /**
         * 语法格式五:若 Lambda 体中只有一条语句, return 和 {} 都可以省略不写
         */
        @Test
        public void test5(){
            Comparator<Integer> com = (x,y) -> Integer.compare(x, y);
            System.out.println(com.compare(3,4));
        }
        /**
         * 语法格式六:Lambda 表达式的参数列表的数据类型可以省略不写,因为JVM 编译器通过上下文推断出,数据类型,即“类型推断”
         */
        @Test
        public void test6(){
            Comparator<Integer> com = (Integer x,Integer y) -> Integer.compare(x, y);
            System.out.println(com.compare(3,4));
        }
    }
    View Code

     类型推断:上述 Lambda 表达式中的参数类型都是由编译器推断得出的。Lambda 表达式中无需指定类型,程序依然可以编译,这是因为 javac 根据程序的上下文,在后台推断出了参数的类型。Lambda 表达式的类型依赖于上下文环境,是由编译器推断出来的。这就是所谓的“类型推断”。

    Lambda表达式的应用示例:

    示例1:对传入的参数,进行运算,在实际调用方法的时候才知晓具体做什么运算。

    @FunctionalInterface
    public interface MyFun {
        /**
         * 根据传入的值,进行运算,返回运算后的值。
         * @param i
         * @return
         */
        public Integer getValue(Integer i);
    }
    
    
    public class TestLambda2 {
        public static void main(String[] args) {
            //先用匿名内部类的方式
            MyFun mf = new MyFun() {
                @Override
                public Integer getValue(Integer i) {
                    System.out.println("我是最原始的匿名内部类实现的运算(返回:参数 * 100)...");
                    return i * 100;
                }
            };
            //对 20 进行 * 100 的运算
            System.out.println(mf.getValue(20));
            System.out.println("-------------------------------");
            //转换成Lambda表达式方式
            MyFun mf1 = (i) -> {
                System.out.println("Lambda 表达式实现运算(返回:参数 * 100)...");
                return i * 100;
            };
            System.out.println(mf1.getValue(20));
    
            //Lambda表达式可以用最简便的代码,实现各种运算
            //例:
            MyFun mf2 = i -> i + 2;
            MyFun mf3 = i -> i * 2;
            MyFun mf4 = i -> i / 2;
            MyFun mf5 = i -> i - 2;
            //等等....................
        }
    }
    View Code

    示例2:用 Collections.sort() 方法对集合排序。

    import lombok.Data;
    
    @Data
    public class Employee{
        private String name;
        private Integer age;
        private Integer gender;
    }
    
    
    import java.util.ArrayList;
    import java.util.Collections;
    import java.util.Comparator;
    import java.util.List;
    
    public class TestLambda3 {
        public static void main(String[] args) {
    
            List<Employee> emps = new ArrayList<>();
            emps.add(new Employee("张三",10,0));
            emps.add(new Employee("李四",13,1));
            emps.add(new Employee("王麻子",8,0));
            emps.add(new Employee("王五",18,0));
            emps.add(new Employee("赵六",13,0));
    
            //原始方法排序
            Collections.sort(emps, new Comparator<Employee>() {
                @Override
                public int compare(Employee o1, Employee o2) {
                    //按照年龄排序
                    return o1.getAge() - o2.getAge();
                }
            });
            System.out.println(emps);
    
            System.out.println("-------------------------------");
            //Lambda表达式方式
            // 排序1:按照年龄排
            Collections.sort(emps,(o1,o2)->o2.getAge() - o1.getAge());
            System.out.println(emps);
            System.out.println("-------------------------------");
            //排序2:如果年龄相同,则按照性别排序
            Collections.sort(emps,(o1,o2)->{
                if((o1.getAge() - o2.getAge()) == 0){
                    return o2.getGender() - o1.getGender();
                }
                return o1.getAge() - o2.getAge();
            });
            System.out.println(emps);
    
        }
    }
    View Code

    当我们需要用Lambda表达式来实现一个自己的需求,就得自己去创建一个接口,这样还是比较麻烦的。实际上,我们完全可以不用自己创建接口,使用Java8自带的函数式接口来满足我们的需求。

    JAVA8内置的四大函数式接口:

    函数式接口  参数类型 返回类型 用途

    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值。包含方法:boolean test(T t);

     

    内置函数式接口示例:

    import org.junit.Test;
    import java.util.ArrayList;
    import java.util.Arrays;
    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 TestLambda4 {
    
        /**
         * Consumer<T> 消费型接口
         */
        @Test
        public void test1(){
            happy(20000.0,m -> System.out.println("你要去做大保健,每次"+m+"元"));
        }
        public void happy(Double money, Consumer<Double> consumer){
            consumer.accept(money);
        }
    
        /**
         * Supplier<T> 供给型接口
         */
        @Test
        public void test2(){
    
            List<Integer> list = getNumList(25, () -> (int) (Math.random() * 100));
            System.out.println(list);
    
        }
        /**
         * 获取指定数量的整数集合
         * @param num
         * @param supplier
         * @return
         */
        public List<Integer> getNumList(int num,Supplier<Integer> supplier){
            List<Integer> list = new ArrayList<>();
            for(int i=0;i<num ;i++){
                Integer result = supplier.get();
                list.add(result);
            }
            return list;
        }
    
        /**
         * Function<T, R> 函数型接口
         */
        @Test
        public void test3(){
            String newStr = strHandler("		
     Java 好牛逼呀   ", (str) -> str.trim());
            System.out.println(newStr);
    
            String subStr = strHandler("Java is so so cool...", (str) -> str.toUpperCase());
            System.out.println(subStr);
        }
        /**
         * 需求:用于处理字符串
         * @param str
         * @param fun
         * @return
         */
        public String strHandler(String str, Function<String, String> fun){
            return fun.apply(str);
        }
    
        /**
         * Predicate<T> 断定型接口
         */
        @Test
        public void test4(){
            List<String> list = Arrays.asList("Java", "Python", "Lambda", "I love u", "ok");
            List<String> strList = filterStr(list, (s) -> s.length() > 4);
            System.out.println(strList);
        }
        /**
         * 返回满足条件的字符串集合
         * @param list
         * @param pre
         * @return
         */
        public List<String> filterStr(List<String> list, Predicate<String> pre){
            List<String> strList = new ArrayList<>();
            for (String str : list) {
                if(pre.test(str)){
                    strList.add(str);
                }
            }
            return strList;
        }
    
    }
    View Code

    其他函数式接口:

    方法引用、构造器引用、数组引用:

    方法引用

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

    方法引用主要的三种语法格式

    对象 :: 实例方法名

    类 :: 静态方法名

    类 :: 实例方法名

    注意:

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

    2. 若 Lambda 参数列表中的 第一参数 是 实例方法的调用者,而 第二个参数 是 实例方法的 参数 时,则可以使用 类 :: 实例方法名(ClassName :: method)

    示例:

    import org.junit.Test;
    import java.util.Comparator;
    import java.util.function.BiPredicate;
    import java.util.function.Consumer;
    import java.util.function.Supplier;
    
    public class TestLambda5 {
        /**
         * 方法引用测试 对象 :: 实例方法名
         */
        @Test
        public void test1(){
            Consumer<String> con = (str) -> System.out.println("我是参数:"+str);
            con.accept("Hello Lambda");
    
            Consumer<String> con1 = System.out::println;
            con1.accept("Hello 方法引用...");
    
            // 需要实现的抽象方法的参数列表和返回值:void accept(String str); accept() 方法的参数为 String ,返回值为 void
            //Lambda体中引用的方法的  参数列表 与 返回值类型 : System.out.println("");System.out 对象的 println() 方法的参数为 String , 返回值为 void
            //在上面这种情况下,(str) -> System.out.println("我是参数:"+str);  就可以直接写成 System.out::println
        }
        /**
         * 方法引用测试 对象 :: 实例方法名
         */
        @Test
        public void test2(){
            Employee emp = new Employee("周三",22,1);
            Supplier<String> sup1 = ()-> emp.getName();
            //Supplier<String> sup1 = emp :: getName; //与上面等价
            System.out.println(sup1.get());
            System.out.println("-------------------------------");
            //Supplier<Integer> sup2 = () -> emp.getAge(); //与下面等价
            Supplier<Integer> sup2 = emp::getAge;
            //sup2.get() 和 emp.getAge() 参数列表都为 Null ,返回值都是Integer ,所以可以用两种表现形式。
            System.out.println(sup2.get());
        }
        /**
         * 方法引用测试 类 :: 静态方法名
         */
        @Test
        public void test3(){
            Comparator<Integer> com1 = (x,y) -> Integer.compare(x,y);
            System.out.println(com1.compare(10,20));
            System.out.println("-------------------------------");
            Comparator<Integer> com2 = Integer::compare;
            System.out.println(com2.compare(122,22));
        }
        /**
         * 方法引用测试 类 :: 实例方法名
         */
        @Test
        public void test4(){
            BiPredicate<String,String> bp1 = (x,y) -> x.equals(y);
            System.out.println(bp1.test("abc","abc"));
            System.out.println("-------------------------------");
            BiPredicate<String,String> bp2 = String::equals;
            System.out.println(bp1.test("abc","abc"));
        }
    }
    View Code

    构造器引用

    与函数式接口相结合,自动与函数式接口中方法兼容。可以把构造器引用赋值给定义的方法,与构造器参数列表要与接口中抽象方法的参数列表一致!

    格式:ClassName :: new

    注意:

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

    示例:

    import org.junit.Test;
    import java.util.function.BiFunction;
    import java.util.function.Function;
    import java.util.function.Supplier;
    
    /**
     *  构造器 引用 测试
     *  格式:ClassName :: new
     */
    public class TestLambda6 {
    
        /**
         * 调用无参构造器
         */
        @Test
        public void test1(){
            Supplier<Employee> supplier = () -> new Employee();
            System.out.println(supplier.get());
            System.out.println("-------------------------------");
            Supplier<Employee> sup1 = Employee::new;//sup1.get() 没有参数,调用的new Employee() 也是无参构造器。
            System.out.println(sup1.get());
        }
        /**
         * 调用一个参数的构造器
         */
        @Test
        public void test3(){
            Function<String,Employee> fun = (name) -> new Employee(name);
            System.out.println(fun.apply("张麻子"));
            System.out.println("-------------------------------");
            Function<String,Employee> fun1 = Employee::new;//fun1.apply("") 有一个参数,调用的new Employee("")一个参数的构造器。
            System.out.println(fun1.apply("王二麻子"));
        }
        /**
         * 调用两个参数的构造器
         */
        @Test
        public void test4(){
            BiFunction<String,Integer,Employee> fun = (name,age) -> new Employee(name,age);
            System.out.println(fun.apply("张麻子",12));
            System.out.println("-------------------------------");
            BiFunction<String,Integer,Employee> fun1 = Employee::new;//fun1.apply("",int) 有两个参数,调用的new Employee("",int)两个参数的构造器。
            System.out.println(fun1.apply("王二麻子",20));
        }
    }
    View Code

    数组引用

    格式:type[] :: new

    示例:

        /**
         * 数组引用
         */
        @Test
        public void test5() {
            Function<Integer, String[]> fun1 = (x) -> new String[x];
            System.out.println(fun1.apply(10).length);
            System.out.println("-------------------------------");
            Function<Integer, String[]> fun2 = String[]::new;
            System.out.println(fun1.apply(20).length);
        }
    View Code
  • 相关阅读:
    JavaScript压缩混淆 / 格式化 / 美化工具
    5.PCA
    4.SVM(基于结构风险最小化)
    3(5).线性分类之朴素贝叶斯
    3(4).线性分类之Gaussian Discriminant Analysis高斯判别模型
    sklearn---Logistic Regression
    3(3).线性分类之logistic regression(基于经验风险最小化)
    2.2 数据结构---数组(查找)
    2.1 数据结构---数组
    3(2).线性分类之线性判别分析(LDA)
  • 原文地址:https://www.cnblogs.com/y3blogs/p/13060212.html
Copyright © 2011-2022 走看看