• Java8_Lambda表达式


            从2014年java8发布到现在已经有几个年头了,现在java11都发布了。公司最近把服务器环境重新搭建了一遍,jdk版本也从7换成了8,终于可以在代码里面写Lambda表达式了。作为一名java开发人员,java8的一些新东西也是必须要掌握的,今天就说说这Lambda表达式的使用。

    一、Lambda表达式简介

    (1)定义
    Lambda是一个匿名函数,我们可以把Lambda表达式理解为一段可以传递的代码。
    优点:可以写出更简洁、更灵活的代码;同时它还是一种更紧凑的代码风格,使java的语言表达能力得到了提升
    (2)看看代码
    我们通过对比new一个Runnable的案例来对比使用Lambda表达式和不使用Lambda表达式的区别,代码如下:
    	@Test
    	public void test01() {
    		// java7 new一个Runnable 匿名内部类
    		Runnable r1 = new Runnable() {
    			@Override
    			public void run() {
    				System.out.println("runnable java7");
    			}
    		};
    		
    		// java8 new一个Runnable  使用了Lambda表达式,代码更紧凑了
    		Runnable r2 = () -> System.out.println("runnable java8");
    		
    		// 开启线程
    		new Thread(r1).start();
    		new Thread(r2).start();
    	}
    	
    	@Test
    	public void test02() {
    		// java7 匿名内部类方式 新开一条线程
    		new Thread(new Runnable() {
    			@Override
    			public void run() {
    				System.out.println("runnable java7 thread");
    			}
    		}).start();
    		
    		// java8   使用了Lambda表达式 替代了匿名内部类,代码更简洁。
    		// 从这也能体现出Lambda表达式是一段可传递的代码(因为Lambda表达式等价于一个匿名内部类对象,所以可以当参数传递)
    		new Thread(() -> System.out.println("runnable java8 thread")).start();
    	}
    代码中new一个Runnable可以通过匿名内部类的方式,也可以采用Lambda的方式。但采用Labmbda方式的代码更简洁,可以让代码更紧凑。同时,匿名内部类对象是可以当成参数来直接传递的,Lambda表达式也可以用来当参数传。这就很好的解释了我们上面对Lambda的定义,即:【1】一个匿名函数,【2】一段可以传递的代码。

    二、Lambda表达式的语法格式

            说语法格式之前,先来看张图吧。
        
        这图的左边是采用匿名内部类形式new的一个Runnable对象,右边是采用Lambda形式new的Runnable对象。对比一下就能领悟到Lambda表达式的语法格式啦。
    (1)Lambda表达式标准格式
    Lambda表达式个标准形式如下图:
    Lambda表达式标准格式:左侧 -> 右侧
        左侧:指定了Lambda 表达式所需要的所有参数,即:形参列表。
        右侧:指定了Lambda 体,即:Lambda表达式要执行的功能。
    (2)Lambda表达式语法格式一
    左侧形参列表可以不需要参数的数据类型,java8后会根据上下文自动进行类型推断。
    也就是说我们在Lambda的形参列表里面写的参数是可以不写数据类型的。具体案例见下图:
    (3)Lambda表达式语法格式二
    左侧形参列表只有一个参数时,小括号可以省略不写。不过一般建议还是写一下。
    具体的案例见下图
    (4)Lambda表达式语法格式三
    左侧形参列表没有参数时,小括号必须保留
    具体参考下面案例
    这也是为什么在(3)中建议一个参数时也把小括号写上,写法统一一下。
    (5)Lambda表达式语法格式四
    右侧方法体中代码有多行时,需要用大括号包起来
    具体看下图:
    (6)Lambda表达式语法格式五
    当右侧方法体中代码只有一行时,大括号可省略,同时return关键字也可以省略。
    具体参考下图:

    三、Lambda表达式vs匿名内部类

            看完这么多例子,感觉能用匿名内部类的地方就能用Lambda表达式。其实是不行的,不是所有能用匿名内部类的地方都可以用Lambda表达式来替代的。
    下面我们来看个案例
    (1)首先随便定义一接口
    public interface TestFun {
        // 这接口里面有2个抽象方法
    	void sayHello();
    	void end();
    }
    
    (2)使用匿名内部类和Lambda表达式来创建这个接口的实例
    	public static void main(String[] args) {
    		// 匿名内部类 == Lambda表达式 ?   no .. 
    		
    		// 匿名内部类
    		TestFun t1 = new TestFun() {
    			@Override
    			public void sayHello() {
    				System.out.println("hello");
    			}
    			@Override
    			public void end() {
    				System.out.println("end");
    			}
    		};
    		t1.sayHello();
    		t1.end();
    		
    		// lambda 创建不了TestFun的实例对象,
    		// TestFun t2 = () -> {}
    		// 提示The target type of this expression must be a functional interface
    		// 因为 lambda表达式需要函数式接口
    		// 那什么是函数式接口,  我们可以去看看几个函数式接口Comparator、Runnable
    		// 函数式接口(Functional Interface)就是一个有且仅有一个抽象方法,但是可以有多个非抽象方法的接口。
    		
    	}
    在写代码过程中发现Lambda表达式创建不了这种接口里面有多个抽象方法的对象,而匿名内部类可以。
    在使用Lambda表达式时eclipse直接提示了一个错误,错误消息见下图:
    这个弹窗的提示说目标类型必须是函数式接口。
    (3)小结
        通过上面这个案例,我们发现并不是所有能用匿名内部类的地方都可以用Lambda表达式来替代。
    Lambda表达式只能用来替代那些函数式接口的匿名内部类。那什么是函数式接口呢?继续往下看吧

    四、函数式接口

    (1)定义
    从java8实战这书中读到了函数式接口的定义如下:
    一言以蔽之,函数式接口就是只定义一个抽象方法的接口
    上面这个定义已经很言简意赅了,函数式接口就是只有一个抽象方法的接口

    (2)说说常用的函数式接口
    直接可以去看jdkjava.util.function包下面看,都是函数式接口。常用的函数式接口如下:
    • 消费型接口Consumer
            它有一个抽象方法,形参列表为一个参数,没有返回值。具体的参考下面代码的案例
        /**
         * 消费型接口Consumer
         */
        @Test
        public void test01() {
            /*
             *  Consumer是一个消费型接口
             *  (1)该接口的抽象方法为void accept(T t);
             *  (2)这个抽象方法形参列表有一个参数,没有返回值;
             *  (3)这个方法就是一个有数据进去,没数据返回的接口; 所以就叫它消费型接口
             */
            Consumer<String> c = (x) -> {
                System.out.println(x);
                System.out.println("length is:" + x.length());
            };
            c.accept("hello");  // 我们传一个hello给它,将会在控制台中打印出hello及它的长度
        }
    • 断言型接口Predicate
            它有一个抽象方法,形参列表为一个参数,返回值为boolean类型。特别适合条件判断的场景,具体的参考下面代码的案例
        /**
         * 断言型接口Predicate
         */
        @Test
        public void test02() {
            /*
             *  Predicate是一个断言型接口
             *  (1)该接口的抽象方法为boolean test(T t);
             *  (2)这个抽象方法形参列表有一个参数,返回值为boolen;
             *  (3)这个方法就是一个有数据进去,返回true或者false; 特别适合用来做条件判断的场景
             */
            // 下面演示一个过滤集合里面元素的案例,过滤掉集合里面小于5的元素
            List<Integer> list = listFilter(Arrays.asList(new Integer[]{12, 2, 3, 10, 25}), x -> x >= 5);
            System.out.println(list);   // 输出 [12, 10, 25]
        }
    
        /**
         * 定义的一个集合过滤方法
         * @param list      集合
         * @param predicate 断言型接口,用来里面包含了过滤的条件
         * @return
         */
        private <T> List<T> listFilter(List<T> list, Predicate<T> predicate) {
            List<T> result = new ArrayList<T>();
            for (T t : list) {
                if (predicate.test(t)) {
                    result.add(t);
                }
            }
            return result;
        }
    
    更多函数式接口,可以直接看java.util.function包里面的内容。


  • 相关阅读:
    IE6下,设置了domain,导致target指向iframe时,无法打开链接的问题及解决
    contains和compareDocumentPosition 方法来确定是否HTML节点间的关系
    xCode4.2中导入OpenFlow库到项目中出现编译警告warning: Semantic Issue: Writable atomic property
    NSXMLParser具体解析xml的应用详解
    CoCoa编程中视图控制器与视图类(一)
    CoCoa编程中视图控制器与视图类(二)
    关于值传递(高手请无视)
    程序员人生之DAN疼篇
    jquery杂谈jquery选择符
    怎么样把 swf格式 转换为flv格式
  • 原文地址:https://www.cnblogs.com/zeng1994/p/32579646a93f70412dd4196ceda1f2ac.html
Copyright © 2020-2023  润新知