zoukankan      html  css  js  c++  java
  • Java基础知识08--lambda表达式与函数式接口详解

    1.什么是Lambda表达式?

    Lambda 表达式,也可称为闭包,它是推动 Java 8 发布的最重要新特性。

    Lambda 允许把函数作为一个方法的参数(函数作为参数传递进方法中)。

    使用 Lambda 表达式可以使代码变的更加简洁紧凑。

    2.使用Lambda的限制条件

    Lambda并不是任何地方都可以使用,Lambda表达式需要“函数式接口”的支持

    3.什么是函数式接口?

    接口中只有一个抽象方法的接口,称为函数式接口,可以用@FunctionalInterface修饰一下,这里需要注意的是:未使用 @FunctionalInterfaces注解的接口未必就不是函数式接口,一个接口是不是函数式接口的条件只有一条,即接口中只有一个抽象方法的接口(Object类中的方法不算)。而使用@FunctionalInterface注解修饰了的接口就一定是函数式接口,添加@FunctionalInterface注解可以帮助我们检查是否是函数式接口。

    函数式接口(Functional Interface)是java8新增的特性,它是一个有且仅有一个抽象方法,但是可以有多个非抽象方法的接口。函数式接口可以被隐式转换为lambda表达式。

    JDK中常见的函数式接口有:

    package java.lang;
    
    @FunctionalInterface
    public interface Runnable {
        void run();
    }
    package java.util.concurrent;
    
    @FunctionalInterface
    public interface Callable<V> {
        V call() throws Exception;
    }

    4.lambda表达式基础语法

     java中,引入了一个新的操作符“->”,该操作符在很多资料中,称为箭头操作符,或者lambda操作符;箭头操作符将lambda分成了两个部分:

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

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

    以下是lambda表达式的重要特征:

    • 可选类型声明:不需要声明参数类型,编译器可以统一识别参数值。
    • 可选的参数圆括号:一个参数无需定义圆括号,但多个参数需要定义圆括号。
    • 可选的大括号:如果主体包含了一个语句,就不需要使用大括号。
    • 可选的返回关键字:如果主体只有一个表达式返回值则编译器会自动返回值,大括号需要指定表达式返回了一个数值。

    4.1 无参数,无返回值

     () -> System.out.println("hello lambda");

    Runnable r = new Runnable() {
        @Override
        public void run() {
            System.out.println("hello runnable");
        }
    };
    r.run();
    Runnable r1 = () -> System.out.println("hello lambda");
    r1.run();

    此时,如果右边的代码简单,只有一行代码时,{}可以省略。

    4.2 有一个参数,无返回值

    public class CalculatorTest {
    
        public void print(Consumer<String> msg) {
            System.out.println(msg);
        }
    
        public void doPrint(String msg) {
            print((str) -> System.out.println(msg));
            print(str -> System.out.println(msg));  // 简写
    
        }
    }

    此时,左边的()可以省略。

    4.3 有两个参数,Lambda体内只有一条语句,且有返回值,return可省略

    public void test3() {
            BinaryOperator<Integer> binary = (x, y) -> x + y;
            System.out.println(binary.apply(1, 2));// 3
        }

    4.4 多个参数,多行语句

        public void test4() {
            // 无返回值lambda函数体中用法
            Runnable r1 = () -> {
                System.out.println("hello lambda1");
                System.out.println("hello lambda2");
                System.out.println("hello lambda3");
            };
            r1.run();
    
            // 有返回值lambda函数体中用法
            BinaryOperator<Integer> binary = (x, y) -> {
                int a = x * 2;
                int b = y + 2;
                return a + b;
            };
            System.out.println(binary.apply(1, 2));// 6
    
        }

    5.典型函数式接口的使用

    Consumer<T> : 消费型接口(无返回值,有去无回)
             void accept(T t);
     Supplier<T> : 供给型接口
             T get();
             
     Function<T,R> : 函数型接口
            R apply(T t);
            
     Predicate<T> : 断言型接口
            boolean test(T t);
            
    四大核心接口的-->扩展子接口

    Predicate接口适合用于过滤,测试对象是否符合某个条件,Predicate接口源码如下:

    @FunctionalInterface
    public interface Predicate<T> {
    
        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);
        }
    }

    可以看到,Predicate接口待实现的唯一抽象方法是 boolean test(T t) 方法。我们用Predicate接口实现从整数型数组中过滤正数:

        public static void main(String[] args)
        {  
            List<Integer> numbers = Arrays.asList(-1, -2, 0, 4, 5);
            filter(numbers, n -> n > 0);
        }
        
        public static void filter(List<Integer> numbers, Predicate<Integer> condition)
        {
            for (Integer number : numbers)
            {
                if (condition.test(number))
                {
                    System.out.println("Eligible number: " + number);
                }
            }
        }

    运行结果如下:

    Eligible number: 4
    Eligible number: 5

    对数组的迭代,还可以使用Stream API的方式:

        public static void main(String[] args)
        {
            List<Integer> numbers = Arrays.asList(-1, -2, 0, 4, 5);
            numbers.stream().filter(n -> n > 0).forEach(n -> System.out.println("Eligible number: " + n));
        }
    上面的代码采用Stream API + Predicate接口 + Consumer接口的方式实现了同样的功能,代码量大大减少。Stream API(java.util.stream)同样是java8的新特性,将真正的函数式编程风格引入到java语言中,进一步简化了代码。

    参考文献:https://www.jianshu.com/p/8d7f98116693

    https://www.cnblogs.com/wuhenzhidu/p/lambda.html

    https://blog.csdn.net/qq_28410283/article/details/80961022

    https://www.runoob.com/java/java8-lambda-expressions.html

  • 相关阅读:
    Linux下面编译安装ffmpeg
    Fidder简单使用方法(HTTPS抓取和url替换)
    关于一下个阶段的计划
    JAVA的随机的字符串的封装(基本上够用了)
    Shell Script中的间接变量引用
    进程概念
    int main(int argc, char *argv[])的解读
    存储数组数据到SharedPreferences
    C语言中的基本声明
    C中关于指针数组的用法
  • 原文地址:https://www.cnblogs.com/luckyplj/p/15213888.html
Copyright © 2011-2022 走看看