zoukankan      html  css  js  c++  java
  • 一文看懂java中的Lambda表达式

    Lambda表达式优于匿名类

    在 Java 8 中,添加了函数式接口, lambda 表达式和方法引用,以便更容易地创建函数对象。今天我们就来聊聊 lambda 表达式。

    Lambda表达式的前世------匿名类

    以往,使用单一抽象方法的接口被用作函数类型。 它们的实例表示函数(functions)或行动(actions)。 自从 JDK 1.1 于 1997 年发布以来,创建函数对象的主要手段就是匿名类。匿名类,通俗地讲,就是没有类名,直接通过new关键字创建这个类的实例。下面是匿名类的一个例子:

    java.util包中的Comparator接口

    public interface Comparator<T> {
    	int compare(T o1, T o2);
    }

    使用匿名类创建排序的比较方法(强制排序顺序):

    Collections.sort(words, new Comparator<String>() {
    	public int compare(String s1, String s2) {
        	return Integer.compare(s1.length(), s2.length());
     }
    });

    匿名类适用于需要函数对象的经典面向对象设计模式,特别是策略模式,上面的匿名类是排序字符串的具体策略。 然而,匿名类确实过于冗长。

     

    Lambda表达式的今生

    在 Java 8 中,语言形式化了这样的概念,即使用单个抽象方法的接口是特别的,应该得到特别的对待。 这些接口现在称为函数式接口,并且该语言允许你使用lambda 表达式或简称 lambda 来创建这些接口的实例。 Lambdas 在功能上与匿名类相似,但更为简洁。 下面的代码使用 lambdas 替换上面的匿名类。清晰明了:

    Collections.sort(words,
       (s1, s2) -> Integer.compare(s1.length(), s2.length()));

    什么?你告诉我还没理解Lambda表达式?

    那我们再来一个简单的例子:

    首先我们定义一个study接口

    @FunctionalInterface
    public interface Learn{
    	void study();
    }

    为了获取 Learn接口的实现对象,可以为该接口定义一个实现类 StudyDemo

    public class StudyDemo implements Learn{
    	@Override    
    	public void study() {    
    		System.out.println("好好学习,天天向上");        
    	}    
    }

    然后创建该实现类的对象调用study方法:

    	Learn s = new StudyDemo ();        
    	s.study();//运行结果-->好好学习,天天向上

    如果StudyDemo 类只是为了实现 Learn接口而存在的,且仅被使用了一次,所以使用匿名内部类来简化这一操作:

    public static void main(String[] args) {    
    	Learn s = new Learn () {        
    	@Override            
    	public void study() {            
    		System.out.println("好好学习,天天向上");                
    		}
    	}; 
    	s.study();//运行结果-->好好学习,天天向上       
    	}    

    显然,使用匿名内部类还不够简洁,所以我们用lambda表达式来优化:

    因为Learn接口中只有一个方法,编译器使用称为类型推断的过程从上下文中推导出这些类型和方法,所以我们可以省去study的方法名和new Learn,并加上箭头 ->

    public static void main(String[] args) {    
    	Learn s = ()->{        
    		System.out.println("好好学习,天天向上");                
    		}
    	}; 
    	s.study();//运行结果-->好好学习,天天向上       
    	}    

    如果像上面一样lambda表达式只有一行代码,我们可以进一步优化:

    public static void main(String[] args) {    
    	Learn s = ()-> System.out.println("好好学习,天天向上");           
    	s.study(); //运行结果-->好好学习,天天向上      
    	}    

    一行代码对于 lambda 说是理想的,三行代码是合理的最大值。 如果违反这一规定,可能会严重损害程序的可读性。 如果一个 lambda 很长或很难阅读,要么找到一种方法来简化它或重构你的程序来消除它。

    至此为止,相信你已经对lambda有所了解,我们再来几个例子加深理解

    上面我们举的例子中,study()方法既没有参数也没有返回值,如果有参数和返回值又该怎么办呢?

    首先定义一个接口中的唯一抽象方法带参数的情况:

    interface Learn1{
        void study(int a,int b);
    }

    再看看它的匿名内部类和lambda表达式写法

    public static void main(String[] args) {
    	//先定义一个接口的引用
    	Learn1 learn;
           //匿名内部类写法
        learn = new Learn1() {
            @Override
            public void study(int a,int b) {
                System.out.println("好好学习x"(a+b));
            }
        };
        oasis.learn.study(1,2);//运行结果-->好好学习x2
    
        //lambda表达式写法,省略接口和方法名,
        //方法的参数类型可以推导出来,也可以省略
        learn = (ab)->{
            System.out.println("好好学习x"(a+b));
        };
        oasis.learn.study(3,4);//运行结果-->好好学习x12

    如果方法中只有一个参数,还可以省略小括号,Lambda表达式中只有一条语句可以省略大括号

        learn = e-> System.out.println("好好学习"+e);
        oasis.learn.study(5);

    再来看看有返回值的情况,再回过头来看java.util包中的Comparator接口

    public interface Comparator<T> {
    	int compare(T o1, T o2);
    }

    先定义一个学生类:

    public class Student {
        private String name;
        private int age;
        public Student(String name,int age){
        	this.name = name;
        	this.age = age;
        }
        public int getAge(
        	return this.age;
    }

    接下来我们对数组中的Student对象,使用Arrays的sort方法通过年龄进行升序

    public static void main(String[] args) {
    	Student[] array = {
             new Student("张三", 18),    
             new Student("李四", 20),    
             new Student("王五", 19) };
     	//匿名内部类写法
        Comparator<Student > compare = new Comparator<Student >() {
                @Override
                public int compare(Student s1, Student s2) {
                    return s1.getAge() s2.getAge();
                }
            };
            //调用
            Arrays.sort(array, compare); 
    
        //lambda表达式写法,去掉接口名和方法名
        Comparator<Student > compare = (Student s1, Student s2)->{
                    return s1.getAge() s2.getAge();
                }
            };
            //调用
            Arrays.sort(array, compare); 
    
        //省略写法,去掉参数类型,只保留返回值
         Comparator<Student > compare = (s1,s2)-> s1.getAge() s2.getAge()

    总结

    Lambda表达式的语法非常简洁,但是使用时有几个问题需要特别注意:

    1. 使用Lambda表达式必须具有接口,且要求接口中有且仅有一个抽象方法。

    2. 使用Lambda必须具有上下文推断。也就是方法的参数或局部变量类型必须为

    Lambda对应的接口类型,才能使用Lambda作为该接口的实例。

     

    Lambda标准形式

    (参数类型 参数名称) ‐> { 代码语句 }

    说明:

    1. 小括号内:没有参数就留空();多个参数就用逗号分隔。

    2. -> 是新引入的语法格式,代表指向动作。

    3. 大括号内的语法与传统方法体要求基本一致。

     

    Lambda的省略:凡是可以根据上下文推导得知的信息,都可以省略

    在Lambda表达式标准形式的基础上:

    1. 小括号内参数的类型可以省略;

    2. 如果小括号内只有一个参数,则小括号可以省略;

    3. 如果大括号内只有一个语句,则无论是否有返回值,都可以省略大括号、return关键字及语句分号。

    综上所述,从 Java 8 开始,lambda 是迄今为止表示小函数对象的最佳方式。 除非必须创建非函数式接口类型的实例,否则不要使用匿名类作为函数对象。

    备注:有且仅有一个抽象方法的接口,称为“函数式接口”。

    参考书目:《Effective Java》

  • 相关阅读:
    什么样的代码称得上是好代码?
    九年程序人生 总结分享
    Docker入门 第一课 --.Net Core 使用Docker全程记录
    阿里云 Windows Server 2012 r2 部署asp.net mvc网站 平坑之旅
    Visual studio 2015 Community 安装过程中遇到问题的终极解决
    Activiti6.0 spring5 工作流引擎 java SSM流程审批 项目框架
    java 进销存 库存管理 销售报表 商户管理 springmvc SSM crm 项目
    Leetcode名企之路
    24. 两两交换链表中的节点
    21. 合并两个有序链表
  • 原文地址:https://www.cnblogs.com/shoshana-kong/p/15119552.html
Copyright © 2011-2022 走看看