zoukankan      html  css  js  c++  java
  • 函数式接口

    函数式接口

    1、概念、格式

    函数式接口在Java中是指只有一个抽象方法的接口,但是接口中还可以有其他方法,比如public、default等,只有确保接口只有一个抽象方法,Lambda才能进行顺利的推导。

    格式

    @FunctionalInterface// 使用注解来检测该接口是否函数式接口,也就是是否只有一个抽象方法
    修饰符 interface 接口名称 {
        public abstract 返回值类型 方法名称(参数列表);
    }
    

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

    package cn.zhuobo.day16.aboutFuntionalInterface;
    
    //函数式接口作为一个方法的参数,有三种用法去调用这个方法
    
    public class MyFunctionalInterfaceMain {
        public static void main(String[] args) {
            
            // 1、创建一个实现类对象传递给show方法,该实现类里重写了抽象方法
            show(new MyFunctionnalInterfaceImpl());
            
            // 2、创建一个匿名内部类,传递给show方法
            show(new MyfunctionalInterface() {
                @Override
                public void method() {
                    System.out.println("这是使用匿名内部类来实现的");
                }
            });
    
            // 3、使用Lambda表达式简化匿名内部类的书写
            show(() -> {
                System.out.println("这是使用Lambda表达式实现的");
            });
        }
    
        public static void show(MyfunctionalInterface myInter) {
            myInter.method();
        }
    }
    

    注意:Lambda表达式在应用上可以看作是一种语法糖,简化了匿名内部类的书写,但是,事实上他他们的原理是不一样的(for each 表达式是简化迭代器书写的一种语法糖,他们的实现原理都是一样,都是使用迭代器),Lambda表达式不会生成一个class文件,但是使用匿名内部类会生成一个xxx$1.class文件。

    2、函数式编程

    2.1 延迟执行,函数式接口作为方法的参数

    有时候,我们的代码的执行结果不会被马上使用,或者没有产生有用的结果,就会发生性能浪费的事情。就像下面的例子一样,一个方法的传递给方法的参数是三个字符串的拼接,也就是拼接字符串出现在判断level == 1之前,也就是说哪怕level != 1,也会初心字符串拼接,但是此时这些拼接的字符串就显得没有意义了,因为根本没有什么用。也就是发生了性能浪费。

    package cn.zhuobo.day16.logger;
    
    public class LoggerDemo1 {
        public static void main(String[] args) {
            String mess1 = "你是";
            String mess2 = "猪猪";
            String mess3 = "吗?";
    
            showMessage(2, mess1 + mess2 + mess3);
        }
        public static void showMessage(int level, String message) {
            if(level == 1) {
                System.out.println(message);
            }
        }
    }
    

    Lambda表达式的延迟执行:把Lambda表达式作为参数传递到方法中,只有满足条件,这样才会执行Lambda表达式的代码块,这样就可以避免性能的浪费。

    一个接口

    package cn.zhuobo.day16.logger;
    
    @FunctionalInterface
    public interface LoggerInterface {
        public abstract String showMessage();// 返回String
    }
    

    性能优化

    package cn.zhuobo.day16.logger;
    
    public class LoggerDemo02 {
        public static void main(String[] args) {
            String mess1 = "你是";
            String mess2 = "猪猪";
            String mess3 = "吗?";
            method(2, () -> {
                return mess1 + mess2 + mess3;
            });
        }
    
        public static void method(int level, LoggerInterface logger) {
            if(level == 1)
                System.out.println(logger.showMessage());
        }
    }
    

    仔细梳理一下就会发现:接口的抽象方法返回的是字符串,根据Lambda表达式,我们可以知道他返回的是一个拼接后的字符串。同时,在method方法中,只有level==1才会调用接口的showMessage方法,这样才会进行字符串的拼接,反之就不会进行字符串拼接。没有性能浪费现象。

    2.2 函数式接口作为方法的返回值

    如果一个方法返回的是一个函数式接口,那么就可以返回一个Lambda表达式(当然也可以返回实现类对象,匿名内部类对象)

    下面的例子是通过一个方法获得一个java.util.Comparator接口类型的对象作为Arrays.sort方法的参数,对数组进行排序,这个方法就返回一个函数式接口。

    package cn.zhuobo.day16.functionalInterfaceReturn;
    
    import java.util.ArrayList;
    import java.util.Arrays;
    import java.util.Comparator;
    
    public class Demo01 {
    
        // 这个方法的返回值是一个函数式接口,那么可以返回一个匿名内部类
        public static Comparator<String> getComparator1() {
            return new Comparator<String>() {
                @Override
                public int compare(String o1, String o2) {
                    return o1.length() - o2.length();// 按照字符串的长度升序排序
                }
            };
        }
        // 这个方法的返回值是一个函数式接口,返回一个Lambda表达式
        public static Comparator<String> getComparator2() {
            return (o1, o2) -> o1.length() - o2.length();
        }
    
        public static void main(String[] args) {
            String[] arr = {"aa", "bbbb", "ccccccccccc", "dd", "adf"};
            System.out.println(Arrays.toString(arr));
    
            Arrays.sort(arr, getComparator2());
            System.out.println(Arrays.toString(arr));
        }
    }
    

    2.3 常用的函数式接口

    java.util.function中包含了大量的函数式接口

    • java.util.function.Supplier

    该接口仅包含一个无参数的方法T get(),用来获取一个泛型参数指定类型的对象数据(生产数据),指定接口的泛型T是什么,那么get方法就会生产(返回)什么类型的数据

    package cn.zhuobo.day16.aboutFuntionalInterface;
    
    import java.util.function.Supplier;
    
    public class InterfaceSupplier {
        public static String getString(Supplier<String> sup) {
            //System.out.println("shshs");
            return sup.get();
        }
    
        public static void main(String[] args) {// getString方法是函数式接口,因此可以传递Lambda表达式
            String str = getString(() -> {return "bilibili";});
            System.out.println(str);
        }
    }
    
    • java.util.function.Consumer

    Consumer接口是和Supplier接口相反,不是生产一个数据,而是消费一个数据,该接口中包含抽象方法void accept(T, t),意思是消费一个数据,泛型T指定的是什么类型就消费什么类型的数据,至于具体怎么消费(也就是怎么使用数据),那就要根据自己的需要的(输出,运算.......)

    package cn.zhuobo.day16.aboutFuntionalInterface;
    
    import java.util.function.Consumer;
    
    public class InterfaceConsumer {
        // 一个函数式接口作为参数
        public static void method(String message, Consumer<String> con) {
            con.accept(message);
        }
    
        public static void method2(String message, Consumer<String> con1, Consumer<String> con2) {
            con1.andThen(con2).accept(message);// 先将两个接口连接到一起再消费数据,但是还是调用者先消费,也就con1先消费
        }
    
        public static void main(String[] args) {
            String message = "Hello";
            // 参数里可以写Lambda表达式,字节决定如何重写accept方法,这里是是字符串翻转
            method(message, (s) -> {
                String reMessage = new StringBuilder(s).reverse().toString();
                System.out.println(reMessage);
            });
    
    
            method2(message, (s) -> {
                System.out.println(s.toUpperCase());
            }, (s) -> {
                System.out.println(s.toLowerCase());
            });
        }
    }
    
    
    • java.util.function.Predicate

    该接口用来对某种类型的数据进行判断,结果返回一个boolean值,可以使用接口的一个抽象方法boolean test(T t)判断,结果符合条件返回true,否则返回false

    Predicate接口中三个默认方法andornegate

    package cn.zhuobo.day16.aboutFuntionalInterface;
    
    import java.util.function.Predicate;
    
    public class InterfacePredicate {
        // 一个条件
        public static boolean stringPredicate1(String message, Predicate<String> pre){
            return pre.test(message);// 调用test对message进行判断
        }
        
        // and方法
        public static boolean stringPredicate2(String message, Predicate<String> pre1, Predicate<String> pre2) {
            //return pre1.test(message) && pre2.test(message);
            // 使用and方法有一样的效果
            return pre1.and(pre2).test(message);
        }
    
        // or方法
        public static boolean stringPredicate3(String message, Predicate<String> pre1, Predicate<String> pre2) {
            //return pre1.test(message) && pre2.test(message);
            // 使用and方法有一样的效果
            return pre1.or(pre2).test(message);
        }
        
        //negate方法
        public static boolean stringPredicate4(String message, Predicate<String> pre){
            return pre.negate().test(message);// 调用test对message进行判断
        }
        
        public static void main(String[] args) {
            String message = "hellollo";
    
            boolean b1 = stringPredicate1(message, (str) -> {// 这里写判断的逻辑
                return str.length() > 6;
            });
            System.out.println(b1);
    
            boolean b2 = stringPredicate2(message, (str)->{
                return str.length() > 6;
            }, (str)->{
                return str.contains("j");
            });
            System.out.println(b2);
        }
    }
    
    • java.util.function.Function<T, R>

    该接口用来根据一个类型的数据的到另一个类型的数据,前者为前置条件,后者为后置条件。该接口最主要的抽象方法是 R apply(T, t),根据T类型的参数获取R类型的参数

    默认方法:andthen,与consumer接口类似的情况,当使用两个Function接口作为方法的参数时,就可以类似的使用(也就是可以使用andThen方法,将两次转换连接到一起,还是一样的先进行andThen的调用者。)

  • 相关阅读:
    switch 语句注意事项
    line-height 和 font-size的关系
    HTTP 缓存
    hashchange事件的认识
    面向对象的写法,见到就添,持续更新。。。
    chrome浏览器开发者工具之同步修改至本地
    history对象的一些知识点
    你不知道的函数节流,提高你的JS性能!
    玩媒体查询,就是这么简单粗暴!
    css中clip-path属性的运用
  • 原文地址:https://www.cnblogs.com/zhuobo/p/10665452.html
Copyright © 2011-2022 走看看