zoukankan      html  css  js  c++  java
  • Java8一Lambda与函数式接口

    关于Lambda表示在工作学习中会经常用到,但并没有全面的去了解。在这里做一个较为详细的记录供以后学习查阅。主要参考Java 8 Lambda 表达式

    引言


    Java8之前,我们在使用Runnale创建线程的时候,经常需要将Runable实例传入new Thread中。一般采用匿名内部类将函数作为参数的形式传入

    new Thread(new Runnable() {
        @Override
        public void run() {
            System.out.println("Before Java8,There is no Lambda expression!");
        }
    }).start();

    之后我们可以使用Java8的新特性Lambda这样写:

    new Thread( () -> System.out.println("In Java8, There is Lambda expression!") ).start();

    这里相当于:

    Runable runable = () -> System.out.println("In Java8, There is Lambda expression!");

    可见Lambda表达式的强大之处,下面将以详细记录.


    1、Java8的Lambda特性

    1、Lambda 表达式,也可称为闭包,它是推动 Java 8 发布的最重要新特性。
    2、Lambda 允许把函数作为一个方法的参数(函数作为参数传递进方法中)。
    3、使用 Lambda 表达式可以使代码变的更加简洁紧凑。

    2、Lambda表达式的语法

    (arg1, arg2...) -> { body }
    (type1 arg1, type2 arg2...) -> { body }

    比如:

    (int a, int b) -> {  return a + b; }
    () -> System.out.println("Hello World");
    (String s) -> { System.out.println(s); }

    3、Lambda表达式的特征

    • 可选类型声明:不需要声明参数类型,编译器可以统一识别参数值。
    Arrays.asList( "a", "b", "d" ).forEach( e -> System.out.println( e ) );

    这里e的类型就是由编译器推理得到的,不需要声明类型,当然也可以声明类型,比如:

    Arrays.asList( "a", "b", "d" ).forEach( ( String e ) -> System.out.println( e ) );
    • 可选的参数圆括号:一个参数无需定义圆括号,但多个参数需要定义圆括号。
    • 可选的大括号:如果主体包含了一个语句,就不需要使用大括号。
    Arrays.asList( "a", "b", "d" ).forEach( e -> {
        System.out.print( e );
        System.out.print( e );
    } );
    • 可选的返回关键字:如果主体只有一个表达式返回值则编译器会自动返回值,大括号需要指明表达式返回了一个数值。并且如果Lambda表达式中的语句块只有一行,则可以不用使用return语句

    如下两个是一样的效果

    Arrays.asList( "a", "b", "d" ).sort( ( e1, e2 ) -> e1.compareTo( e2 ) );
    Arrays.asList( "a", "b", "d" ).sort( ( e1, e2 ) -> {
        int result = e1.compareTo( e2 );
        return result;
    } );

    4、Lambda变量作用域

    Lambda表达式只能引用外部被final修饰的局部变量,换句话说在Lambda表达式中我们不能修改定义在外的局部变量。

    public class LambdaTest {
    
        public static void main(String[] args) {
            //final如果不写的话,Lambda表达式会默认该变量为final
            final String string="Hello ";
    
            HelloWorld test = (String message)->System.out.println(message+"World!");
            test.print(string);
        }
        @FunctionalInterface
        interface HelloWorld {
            void print(String str);
            default void print2() {
    
            }
        }
    }

    5、函数式接口

    (1)在学习函数式接口之前,我们先了解Java8下接口的静态方法与默认方法。

    @FunctionalInterface
        interface HelloWorld {
            void print(String str);
            //默认方法,通过实现类直接调用,不需要实现类实现该方法,提高了扩展性!
            default void print2(String message) {
                System.out.println(message);
            }
            //静态方法,直接接口调用。跟普通的static方法是一样的,不需要实现类去调用
            static void print3(String message) {
                System.out.println(message);
            }
        }
    • 默认(default)方法只能通过接口的实现类来调用,不需要实现方法,也就是说接口的实现类可以继承或者重写默认方法。
      这里写图片描述

    • 静态(static)方法只能通过接口名调用,不可以通过实现类的类名或者实现类的对象调用。就跟普通的静态类方法一样,通过方法所在的类直接调用。
      这里写图片描述

    (2)了解了Java8的静态方法与默认方法之后,我们可以准确理解函数式接口的定义与特性:

    • 函数式接口(FunctionalImplement)是一种只含有一个抽象方法声明的接口,但是可以有多个非抽象方法的接口。类似于Java中的Marker Interface标记类型接口,比如java.io.Serializable等都是没有方法声明或属性声明的接口,主要用于通过instanceof就直接能探测一个实例是否是一个特定接口的实例。
    • 那么java.lang.Runable即是一种函数式接口,在接口中只有void run()方法。
    • 简单来说之前我们使用的匿名内部类传入参数,实则是使用匿名内部类来实例化函数式接口的对象。而之后的Lambda表达式可以更进一步简化代码

    我们在看引言当中利用Runable创建线程的例子,后面写道:

    Runable runable = () -> System.out.println("In Java8, There is Lambda expression!");

    其实这里就是利用Lambda表达式实现实例化函数式接口的对象,比如我们自定义一个函数式接口,增加注解@FunctionalInterface,也可以不用注解,只需要符合函数式接口的要求,否则会报错!

    @FunctionalInterface
    interface HelloWold {
        void print();
    }

    定义好函数式接口之后,我们可以使用Lambda Expression

    public static void main(String[] args) {
        //实现函数式接口的对象
        HelloWold test = ()->System.out.println("HelloWorld!");
        test.print();
    }

    如果接口不符合要求,则会报错:
    这里写图片描述

    6、断言(Predicate)函数式接口

    Predicate函数式接口的主要作用就是提供一个test方法,接受一个参数返回一个布尔类型,PredicateStream API中进行一些判断的时候经常用到。主要用于过滤一些符合条件的逻辑,除此之外还会提供三个defalut方法与一个static方法,具体请看源代码:

    @FunctionalInterface
    public interface Predicate<T> {
    
        /**
         * Evaluates this predicate on the given argument.
         *
         * @param t the input argument
         * @return {@code true} if the input argument matches the predicate,
         * otherwise {@code false}
         */
        boolean test(T t);
    
        default Predicate<T> and(Predicate<? super T> other) {
            Objects.requireNonNull(other);
            return (t) -> test(t) && other.test(t);
        }
    
        default Predicate<T> negate() {
            return (t) -> !test(t);
        }
    
        default Predicate<T> or(Predicate<? super T> other) {
            Objects.requireNonNull(other);
            return (t) -> test(t) || other.test(t);
        }
    
    
        static <T> Predicate<T> isEqual(Object targetRef) {
            return (null == targetRef)
                    ? Objects::isNull
                    : object -> targetRef.equals(object);
        }
    }

    这里写图片描述

    其中经常使用的是default方法test判断输入是否符合某个条件,下面是简单的例子。

    //利用test默认方法判断x输入是否大于10
    Predicate<Integer> boolValue = x -> x > 10;    
    System.out.println(boolValue.test(1));//false    
    System.out.println(boolValue.test(11));//true

    Predicate断言通常跟Stream一起复合使用。

    public class PredicateTest {
        public static void main(String[] args) {
            List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
            PredicateTest predicateTest = new PredicateTest();
            //输出大于5的数字
            List<Integer> result = predicateTest.conditionFilter(list, integer -> integer > 5);
            result.forEach(System.out::println);
                System.out.println("-------");
            //输出小于5的数字
            result = predicateTest.conditionFilter(list, integer -> integer < 5);
            result.forEach(System.out::println);
            System.out.println("-------");
            //输出所有数字
            result = predicateTest.conditionFilter(list, integer -> true);
            result.forEach(System.out::println);
            System.out.println("-------");
        }
        //结合使用Stream filter/collect与Predicate
        //filter过滤条件,collect将stream流转换List
        public List<Integer> conditionFilter(List<Integer> list, Predicate<Integer> predicate){
            return list.stream().filter(predicate).collect(Collectors.toList());
        }
    }
    原文地址:https://blog.csdn.net/Mynewclass/article/details/80169476
  • 相关阅读:
    工程事故与现实世界(续)
    工程事故与现实世界
    直觉与概率
    有一种冲动:世界那么大
    人生的意义
    这些年来什么才是最好的投资?
    时间太少,如何阅读?
    程序员的成长法则与进阶攻略
    秘密:从程序员到领导者的微妙之处
    为什么加入付费社群?
  • 原文地址:https://www.cnblogs.com/jpfss/p/11983925.html
Copyright © 2011-2022 走看看