java 函数式接口 详解

1.函数式接口

1.1 概念

      函数式接口在java中是指只有一个抽象方法的接口。

      函数式接口,就是适用于函数式编程场景的接口。在java中函数式编程就体现在Lambda,因此函数式接口就是能够适用于lambda使用的接口。只有确保接口中有且仅有一个抽象方法,lambda才能进行顺利的推导。

1.2 格式

修饰符 interface 接口名称 {
     public abstract 返回值类型 方法名称(可选参数信息);
      // 其他非抽象方法内容
}

       因为接口中抽象方法的修饰符可以省略。例;

public interface MyFunctionalInterface {
           void myMethod();
}

  1.3 @FunctionalInterface注解

      此注解和@override注解的作用类似。该住处应用于函数式接口的定义上。

@FunctionalInterface
public interface MyFunctionalInterface {
      void myMethod();
}

    一旦使用了该注解来定义函数式接口,编译器就会检查该接口是否是有且仅有一个抽象方法。

1.4 自定义函数式接口

    将函数式接口作为方法的参数。

public class Demo {
    private static void dos(FunctionInterface fi){
        fi.method();
    }

    public static void main(String[] args) {
        Demo.dos(()->{System.out.println("lambda表达式");});
    }
}

  

2.函数式编程

2.1 lambda的延迟执行

   有些代码执行后,结果不一定会被使用,进而就是造成资源的浪费,lambda表达式是延迟执行的,这就可以可以解决这种资源浪费的问题。

  性能浪费的日志案例:

     日志可以帮助我们快速的解决定位的问题,记录长须过程中的情况,以便瞠目的监控和优化。

 一种典型的场景的就是对参数进行有条件的使用。比如在对日志信息进行拼接之后,在满足条件的情况下进行打印输出:

public class DemoLogger {
    public static void log(int level,String msg){
        if(level==1){
            System.out.println(msg);
        }
    }

    public static void main(String[] args) {
        String m="你好";
        String s="日志";
        String g="吗?";
        log(1,m+s+g);
    }
}

   这段代码就存在资源浪费的问题,因为不管level是够满足要就,系统会都会把这三个字符串拼接起来,作为参数,传到方法中。

   lambda表达式的更优写法

     lambda表达式的使用前提必须要有函数式接口。

public class DemoLogger1 {
    private static void log(int level,Message msg){
        if(level==1){
            System.out.println(msg.pinjie());
        }
    }

    public static void main(String[] args) {
        String m="你好";
        String s="日志";
        String g="吗?";
        log(1,()->{return m+s+g;});
    }
}

     这个代码只有在level满足要求时,才会对三个字符串进行拼接。

验证Lambda表达式的延迟性

public class DemoLogger1 {
    private static void log(int level,Message msg){
        if(level==1){
            System.out.println(msg.pinjie());
        }
    }

    public static void main(String[] args) {
        String m="你好";
        String s="日志";
        String g="吗?";
        log(2,()->{
            System.out.println("lambda表达式执行了");
            return m+s+g;});
    }
}

   从上述代码可以看出,level不满足条件,lambda表达式没有执行。

2.2 lambda表达式作为参数和返回值

   lambda表达式的原理实际上就是匿名对象。如果方法的参数是一个函数接口,那就该函数接口就可以被lambda表示替代。

  例如:java.lang.Runnable接口就是一个函数式接口。现在有一个方法使用该接口作为参数,那么就可以使用lambda表示进行提到。

public class DemoRunnable {
    private static void startThread(Runnable runnable){
        new Thread(runnable).start();
    }

    public static void main(String[] args) {
        startThread(()->{
            System.out.println("线程执行了!");
        });
    }
}

 如果一个方法的返回值类型是函数式接口,那么就可以直接返回一个lambda表达式。

   例如,需要一个方法来获取java.util.Comparator接口类型的对象作为排序器时候,就可以调用该方法获取。

public class DemoComparator {
    private static Comparator<String> newComparator(){
        return (a,b)->{return a.length()-b.length();};
    }

    public static void main(String[] args) {
        String[] arr={"abc","acvd","a"};
        System.out.println(Arrays.toString(arr));
        Arrays.sort(arr,newComparator());
        System.out.println(Arrays.toString(arr));
    }
}

3.常用函数式接口

3.1 Supplier接口

      java.util.function.Supplier<T>接口仅包含一个无参的方法:T get()。用来获取一个泛型参数指定类型的对象数据。这是一个函数式接口,对应的lambda表达式就需要“对外提供”一个符合泛型类型的对象数据。

public class DemoSupplier {
    private static String getstring(Supplier<String> supplier){
        return supplier.get();
    }
    public static void main(String[] args) {
        String msg="你好";
        String getstring = getstring(() -> { return msg; });
        System.out.println(getstring);
    }
}

3.2 练习:求数组元素的最大值

      使用Supplier接口作为方法的参数,通过lambda表达式求出int数组中的最大值。

public class DemoSupplier {
    private static int getmax(Supplier<Integer> supplier){
        return supplier.get();
    }
    public static void main(String[] args) {
        int[] arr={1,3,4,6,9};
       Integer getmax = getmax(() -> {
           int max=arr[0];
           for (int i = 0; i < arr.length; i++) {
               if(max>arr[i]){
                   max=max;
               }else{
                   max=arr[i];
               }
           }
           return max;
        });
        System.out.println(getmax);
    }
}

3.3 Consumer接口

      java.util.function.Consumer<T>接口正好与Supplier接口相反,他不是生产一个数据,而是消费一个数据,其数据类型根据泛型决定。

  抽象方法:accept

    Consumer接口中包含抽象方法void accpet(T t),消费一个指定的泛型。

public class Democonsume {
    private static void consunmerString(Consumer<String> f){
        f.accept("你好");
    }

    public static void main(String[] args) {
        consunmerString((s)->{System.out.println(s);});
    }
}

    默认方法:andThen

    如果一个方法的参数和返回值都是Consumer类型。 那么在消费数据时,会做一个操作,然后在做一个操作,实现组合。这个方法就是Consumer接口中的default方法andThen。

   要实现组合,就需要两个或者多个lambda表达式,andThen的语义就是一步一步的执行;

public class DemoandThen {
    private static void consumerstring(Consumer<String> c1,Consumer<String> c2){
        c1.andThen(c2).accept("nihao");
    }

    public static void main(String[] args) {
        consumerstring((s)-> System.out.println(s.toUpperCase()),
                        s -> System.out.println(s.toLowerCase()));
    }
}

     运行结果是先输出大写的nihao,然后打印小写的nihao。

3.4 格式化打印

        下面的字符串数组当中存有多条信息,请按照格式“ 姓名:XX。性别:XX。”的格式将信息打印出来。要求将打印姓名的动作作为第一个Consumer 接口的Lambda实例,将打印性别的动作作为第二个Consumer 接口的Lambda实例,将两个Consumer 接口按照顺序“拼接”到一起。

public class DemoandThen {
    private static void consumerstring(Consumer<String> c1,Consumer<String> c2,String[] arr){
        for (String s : arr) {
            c1.andThen(c2).accept(s);
        }

    }

    public static void main(String[] args) {
        String[] arr={"路飞,男","娜美,女","索隆,男","乔巴,动物"};
        consumerstring(s -> System.out.print(s.split(",")[0]+"性别:"),
                       s -> System.out.println(s.split(",")[1]),
                       arr);
    }
}
路飞性别:男
娜美性别:女
索隆性别:男
乔巴性别:动物

  3.5 Predicate接口

       java.util.function.Predicate<T> 接口,对某种类型的数据进行判断,从而得到一个boolean值结果。

 抽象方法:test

        Predicate 接口中包含一个抽象方法: boolean test(T t) 。用于条件判断的场景:

public class DemoPredicate {
    public static void predicate(Predicate<String> predicate){
        boolean test = predicate.test("我是字符串");
        System.out.println(test);
    }

    public static void main(String[] args) {
        predicate(s->s.length()>3);
    }
}
true

       条件判断的标准是传入的Lambda表达式逻辑,只要字符串长度大于3则true.

默认方法:and

    predicate是条件判断,就会有三种逻辑关系,与、或、非。其中将两个Predicate 条件使用“与”逻辑连接起来实
现“并且”的效果时,可以使用default方法and 。

public class DemoPredicate {
    public static void predicate(Predicate<String> p1,Predicate<String> p2){
        boolean test = p1.and(p2).test("我是字符串");
        System.out.println(test);
    }

    public static void main(String[] args) {
        predicate(s->s.length()>3,
                  s->s.length()<6);
    }
}
true

     默认方法:or

                  与and方法同类型

public class DemoPredicate {
    public static void predicate(Predicate<String> p1,Predicate<String> p2){
        boolean test = p1.or(p2).test("我是字符串");
        System.out.println(test);
    }

    public static void main(String[] args) {
        predicate(s->s.length()>3,
                  s->s.length()>6);
    }
}
true

  默认方法:negate

       它是执行了test方法之后,对结果boolean值进行“!”取反而已。一定要在test 方法调用之前
调用negate 方法,正如and 和or 方法一样:

public class DemoPredicate {
    public static void predicate(Predicate<String> p1){
        boolean test = p1.negate().test("我是字符串");
        System.out.println(test);
    }

    public static void main(String[] args) {
        predicate(s->s.length()>3
                  );
    }
}
false

   3.6 集合信息筛选

     数组当中有多条“姓名+性别”的信息如下,请通过Predicate 接口的拼装将符合要求的字符串筛选到集合ArrayList 中,需要同时满足两个条件:

                    1. 必须为女生;

                    2. 姓名为4;

public class DemoPredicate {
    public static ArrayList<String> predicate(Predicate<String> p1, Predicate<String> p2, String[] arr){
        ArrayList<String> a=new ArrayList<>();
        for (String s : arr) {
            if(p1.and(p2).test(s)){
                a.add(s);
            }
        }
        return a;
    }

    public static void main(String[] args) {
        String[] array = { "迪丽热巴,女", "古力娜扎,女", "马尔扎哈,男", "赵丽颖,女" };
        ArrayList<String> pre = predicate(s -> "女".equals(s.split(",")[1]),
                s -> s.split(",")[0].length() == 4,
                array
        );
        System.out.println(pre);
    }
}
[迪丽热巴,女, 古力娜扎,女]

3.7 Function 接口

      java.util.function.Function<T,R> 接口用来根据一个类型的数据得到另一个类型的数据,前者称为前置条件,后者称为后置条件。

     抽象方法:apply

      Function接口中最主要的抽象方法为:R apply(T t),根据类型T的参数获取类型R的结果。

     例如:将String类型转换成Integer类型。

public class DemoFunction {
    public static void method(Function<String,Integer> f){
        Integer apply = f.apply("100");
        System.out.println(apply);
    }

    public static void main(String[] args) {
        method(s-> Integer.parseInt(s));
    }
}
100

  默认方法:andThen

      Function 接口中有一个默认的andThen 方法,用来进行组合操作,该方法同样用于“先做什么,再做什么”的场景,和Consumer 中的andThen 差不多:

public class DemoFunction {
    public static void method(Function<String,Integer> f1,Function<Integer,Integer> f2){
        Integer apply = f1.andThen(f2).apply("100");
        System.out.println(apply);
    }

    public static void main(String[] args) {
        method(s-> Integer.parseInt(s), s->s*=100);
    }
}
1000

    3.8 自定义函数模型拼接

       请使用Function 进行函数模型的拼接,按照顺序需要执行的多个函数操作为:

              String str = "赵丽颖,20";

          1.将字符串截取数字年龄部分,得到字符串;

          2. 将上一步的字符串转换成为int类型的数字;

          3. 将上一步的int数字累加100,得到结果int数字。

public class DemoFunction {
    public static int method(Function<String,String> f1,
                             Function<String,Integer> f2,
                             Function<Integer,Integer> f3,String str){
        return f1.andThen(f2).andThen(f3).apply(str);
    }

    public static void main(String[] args) {
        String str="赵丽颖,20";
        int method = method(s ->s.split(",")[1],
                s -> Integer.parseInt(s),
                s -> s += 100,
                str
        );
        System.out.println(method);
    }
}
120

  

         

 

 

 

 

 

 

     

  

 

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Java 8 引入了函数式编程的概念,其最重要的两个特性是 Lambda 表达式和 Stream API。下面将对这两个特性进行详细介绍。 1. Lambda 表达式 Lambda 表达式是一种匿名函数,它可以作为参数传递给方法或存储在变量Lambda 表达式的语法如下: ``` (parameter1, parameter2, ..., parameterN) -> { statement1; statement2; ... } ``` 其,参数列表可以为空,也可以包含多个参数;箭头符号 "->" 用于将参数列表和 Lambda 表达式的主体分开;体可以是一个语句块,也可以是一个表达式。 下面是一个 Lambda 表达式的例子: ```java (int x, int y) -> { return x + y; } ``` 这个 Lambda 表达式接受两个整数参数 x 和 y,返回它们的和。 Lambda 表达式可以用于函数式接口,即只包含一个抽象方法的接口。例如,下面是一个函数式接口的定义: ```java interface MyInterface { int myMethod(int x, int y); } ``` 可以使用 Lambda 表达式来实现这个接口: ```java MyInterface obj = (int x, int y) -> { return x + y; }; ``` Lambda 表达式还可以使用方法引用来简化代码。例如,可以使用静态方法引用来实现上面的例子: ```java MyInterface obj = Integer::sum; ``` 2. Stream API Stream API 是一种用于处理集合的 API,它提供了一种流式处理集合的方式。Stream API 可以用于对集合进行过滤、映射、排序等操作。 下面是一个使用 Stream API 进行过滤和映射的例子: ```java List<String> list = Arrays.asList("apple", "banana", "orange", "pear"); List<String> result = list.stream() .filter(s -> s.startsWith("a")) .map(String::toUpperCase) .collect(Collectors.toList()); ``` 这个例子首先创建了一个包含四个字符串的列表,然后使用 stream() 方法将其转换为一个流。接着使用 filter() 方法过滤出以字母 "a" 开头的字符串,再使用 map() 方法将这些字符串转换为大写形式。最后使用 collect() 方法将结果收集到一个列表

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值