函数式接口及Lamda表达式详解

文章介绍了Java中的函数式接口,如Function、Predicate、Consumer和Supplier,以及Lambda表达式的使用,强调了它们在函数式编程中的应用,可以简化代码并提高开发效率,但可能影响代码调试和阅读性。
摘要由CSDN通过智能技术生成

函数式接口基本介绍

1、函数式接口:只有一个方法的接口;
2、有且仅有一个抽象方法的接口,但是可以有多个非抽象方法的接口。函数式接口可以被隐式转换为 lambda 表达式。Lambda就是java中函数式编程的体现;
3、主要分布在iava.util.function 包下,常见的 4大原始函数 接口为: Funtion (函数型接口)、Predicate 断定型接口)、 Consumer (消费型接口)、Supplier (供给型接口)

使用场景 

  • 在函数式编程场景中进行使用,java中的函数式编程体现就是Lambda,所以函数式接口就是可以适用于Lambda使用的接口;
  • 函数作为参数,匿名内部类替代,可以简化代码,提高编码效率;
  • 延迟加载,即满足条件才会执行,不满足条件就不执行。这里简单提一下延迟加载含义:

    1.及早求值,对象在定义时便已获取,若后续逻辑并未使用该对象,则会造成资源浪费,降低运行效率。

    2.而惰性求值会在数据真正被调用时去获取,若后续未调用则不获取,避免计算开销,若后续调用多次,也可通过存储计算结果的方式来实现结果复用,避免多次计算。

@Functionallnterface注解

  1. java 8中专门为函数式接口引入了一个新的注解: @Funtionallnterface 。该注解放在接口上,表示此接口是一个函数式接口。并目提示编译器去检查接口是否仅包含一个抽象方法,即,是否符合函数式编程的定义;
  2. 注意:如果自定义一个符合规范的函数式接口,也可以不加@Functionalinterface注解,此注解只是起到一个提示编译器进行规范检查的作用:
  3. 日常开发中用的最多的函数式接口的,比如线程中的 Runnable,还有Callable.
    @FunctionalInterface
    public interface Runnable {
       public abstract void run();
    }

常见的函数式接口使用

Function (函数型接口)

有一个输入参数,有一个输出,apply ()方法就是该接口的唯一方法,也就是继承该Function接口,唯一需要实现的方法,代码示例:

// 输出输入的参数:有一个输入参数,和一个输出
    public static void main(String[] args) {
         //1,初始化,并且实现该接口的唯一实现方法
        Function<String,String> function = new Function<String, String>()  {
            @Override
            public String apply(String param) {
             return param;
           }
         };
         System.out.println(function.apply("abc"));
    }

Lambda就是Java中函数式编程的体现 也就是说只要是函数式接口,就可以使用lambda表达式来简化代码,如下:

public static void main(String[] args) {
        // 使用lambda表达式
        Function<String,String> function = (str)->{return str;};
        //或者我们可以更简单点,把str的()括号去掉也是可以的
        // Function<string,string> function = str->{return str;};
        System.out.println(function .apply("abc")); ;
    }

典型的lambda表达式语法:()->{}; str是传入的参数

Predicate (断定型接口)

有一个输入参数,返回值只能是布尔值,代码示例:

 public static void main(String[] args) {
        Predicate<Integer> predicate = integer -> {
            if (integer > 0) {
                return true;
            }
            return false;
            };
        predicate.test(3);
    }

Consumer(消费型接口)

只有入参,没有返回值

public static void main(String[] args) {
        Consumer<String> consumer = str-> System.out.println(str);
        consumer .accept("abc");
}

Supplier (供给型接口)

没有参数,只有返回值

public static void main(String[] args) {
        Supplier<Integer> supplier = ()-> 1024;
        System.out.println(supplier .get()); ;
}

四大函数式接口比较

函数式接口对应程序逻辑的抽象具体场景
Function程序中映射逻辑的抽象比如我们写得很多的函数: 接收入参,返回出参,方法代码块就是一个映射的具体逻辑。
Predicate程序中判断逻辑的抽象比如各种if判断,对于一个参数进行各种具体逻辑的判定,最后返回一个if else能使用的布尔
Consumer程序中的消费型逻辑的抽象就比如Collection体系的ForEach方法,将每一个元素取出,交给Consumer指定的消费逻辑进行消费
Suppiler程序中的生产逻辑的抽象就比如最常用的,new对象,这就是一个很经典的生产者逻辑,至于new什么,怎么new,这就是Suppiler中具体逻辑的写法了

Lambda 表达式

  1. Lambda表达式(闭包): java8的新特性,lambda运行将函数作为一个方法的参数,也就是函数作为参数传递到方法中。使用ambda表达式可以让代码更加简洁;
  2. lambda表达式,其实本质来讲,就是一个匿名函数。因此在写lambda表达式的时候,不需要关心方法名是什么。实际上,我们在写lambda表达式的时候,也不需要关心返回值类型:
  3. 接口实现,可以有很多种方式来实现。例如: 设计接口的实现类、使用匿名内部类。但是lambda表达式,比这两种方式都简单lambda表达式,只能实现函数式接口。

匿名内部类

应用场景

  • 匿名内部类没有类名,只被使用一次,使代码更简洁:
  • 匿名内部类是在同一条语句中声明和创建的,无法在别的地方实例化和使用这个类;
  • 匿名内部类也可用于接口 interface) 的实现,方便编写事件驱动程序及线程代码。 

使用示例

定义一个接口HelloFunction,包含一个sayHello方法

@FunctionalInterface
public interface He11oFunction {
        void sayHe11o(String he11o);
}

如何需要调用HelloFunction接口中的sayHello方法,传统的方式就是必须定义一个HelloFunction的实现类,然后通过实现类来进行方法调用,如下:

public class HelloFunctionImp1 implements He11oFunction {
        @Override
        public void sayHe11o(String he11o) {
            System.out.println(he11o); ;
        }
}

如果我们只是想单纯的使用一次sayHello方法,不需要创建对象的话,则上面方法略显古板,则可以使用匿名内部类或者下面的局部类
 

    // 匿名内部类
    public void anonymousInner(String he11o){
        He11oFunction he1loFunction = new He11oFunction(){

            @Override
            public void sayHe11o(String he11o) {
                System.out.println(he11o); ;
            }
        };
        he1loFunction.sayHe11o(he11o) ;
    }
    // 局部类
    public void implementsClass(){
        class He11o implements He11oFunction{

            @Override
            public void sayHe11o(String he11o) {
                System.out.println("扩展类:"+he11o);
            }
        }
        He11o he17o = new He11o() ;
    }

Java匿名内部类的注意事项

  • 使用匿名内部类时,必须是继承一个类或者实现一个接口,但是两者不可兼得,同时也只能继承一个类或者实现一个接口:
  • 匿名内部类中是不能定义构造函数的:
  • 匿名内部类中不能存在任何的静态成员变量和静态方法:
  • 匿名内部类为局部内部类,所以局部内部类的所有限制同样对匿名内部类生效。
  • 匿名内部类不能是抽象的,它必须要实现继承的类或者实现的接口的所有抽象方法

Lambda基本语法 

1、() -> 代表了lambda的一个表达式

    /**
     * 无参无返回
     */
    public interface Test01 {
        void test();
    }
    public static void main(String[] args) {
        
        // 具体使用
        Test01 test01 = ()->System.out.println("he11o");
        test01.test();
    }

2、单行代码无需写return (无论函数式接口有没有返回值),花括号

    /**
     * 有参有返回
     */
    public interface Test02 {
        Integer test(Integer a);
    }

    public static void main(String[] args) {
        // 具体使用
        Test02 test02 = (a) -> a + 1;
        test02 .test(12);
    }

3、多行代码必须写花括号,有返回值的一定要写返回值

public static void main(String[] args) {
        // 具体使用
        Test02 test02 = (a) -> {
            System.out.println("多行代码要带花括号和返回值");
           return   a + 1;
        };
        test02 .test(12);
    }

4、单行代码且有参数的情况下可以不写() 如 s-System.out.println(s)

5、(T t)中的参数类型可写可不写。

Lambda 函数引用

lambda表式是为了简化接口的实现的。在lambda表达式中,不应该出现比较复杂的逻辑,如果在lamda表达式中需要外理逻辑食杂,可以单独写一个方法。在lambda表达式中直接引用这个方法即可。

函数引用: 引用一个已经存在的方法,使其替代lambda表达式完成接口的实现 

静态方法的引用

语法:类::静态方法
注意事项:
在引用的方法后面,不要添加小括号。
引用的这个方法,参数(数量、类型) 和返回值,必须要跟接口中定义的一致

使用示例:

public static void main(String[] args) {
        //实现多个参数,一个返回值的接口
        //对一个静态方法的引用,语法:类::静态方法
        Test test = Calculator::calculate;
        System.out.println(test.add(4,5));
    }
    class Calculator{
        public static int calculate(int a,int b ){
            if (a> b) {
                return a - b;
            }
            return b - a;
        }
    }
    interface Test{
        int add(int a,int b);
    }

非静态方法的引用

语法:对象::非静态方法
注意事项:
在引用的方法后面,不要添加小括号。
引用的这个方法,参数(数量、类型) 和 返回值,必须要跟接口中定义的一致。

使用示例:

public static void main(String[] args) {
        //实现多个参数,一个返回值的接口
        //对非静态方法的引用,需要使用对象来完成
        Test test =new Calculator()::calculate;
        System.out.println(test.add(4,5));
    }
    static class Calculator{
        public  int calculate(int a,int b ){
            if (a> b) {
                return a - b;
            }
            return b - a;
        }
    }
    interface Test{
        int add(int a,int b);
    }

构造方法的引用

如果某一个函数式接口中定义的方法,仅仅是为了得到一个类的对象。此时我们就可以使用构造方法的引用,简化这个方法的实现。

语法: 类名::new
使用示例:

    // 定义一个Person类
    public class Person {
        String name;
        int age;
        public Person(String name,int age){
            this.name = name;
            this.age = age;
        }
    }
    //定义一个函数式接口,用以获取对象
    @FunctionalInterface
    private interface GetPerson{
        Person test(String name,int age);
    }
    // 具体使用
    GetPerson lm2 = Person::new;
    Person person = lm2.test("小红",28);

Lambda表达式使用示例

    @FunctionalInterface
    public interface He11oFunction {
        void sayHe11o(String he11o);
    }
    // lambda表达式
    public void lambda(String he11o){
        He11oFunction helloFunction = System.out::println;
        helloFunction.sayHe11o(he11o) ;
    }

注意:如果在lambda表达式中,使用到了局部变量,那么这个局部变量会被隐式的声明为 final。是一个常量,不能修改值

Lambda表达式与匿名内部类对比

1、匿名内部类:可以是接口,也可以是抽象类,还可以是具体类;
2、Lambda表达式,只可能是接口,如果接口中仅有一个抽象方法,可以使用Lambda表达式,也可以使用匿名表达式
3、如果接口中有多个抽象,只能使用匿名内部类。

总结 

  • 函数式接口应用在函数式编程中,Lambda表达式是函数式编程的体现
  • 通过函数式接口和Lambda表达式可以简化代码,提高开发效率。
  • 同时可能会不利于代码调试,或者对于不熟悉函数式编程的同事不容易阅读对应代码。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值