函数式接口

函数式接口

  • 有且仅有一个抽象方法的接口称为 函数式接口

  • 为了确保函数式接口有且仅有一个抽象方法,要使用注解@FunctionalInterface,如果出现多于一个抽象方法或者没有抽象方法会编译报错

  • 函数式接口一般作为方法的参数和返回值类型使用

  • 使用函数式接口的四种方式:

    接口:

    public interface Action {
    void run(String sth);
    }

    实现类:

    public class ActionImpl implements Action{
    @Override
    public void run(String sth) {
    System.out.println("say: " + sth);
    }
    }

    Demo

    public class DemoInterface {
    public static void showRun(Action act){
    act.run("实现类使用方法");
    }
    public static void main(String[] args) {
    // 1.实现类对象
    showRun(new ActionImpl());

    // 2. 匿名内部类
    showRun(new Action() {
    @Override
    public void run(String sth) {
    System.out.println("使用匿名内部类使用方法");
    }
    });
    // 3. Lambda
    showRun((String sth)->{
    System.out.println("使用Lambda表达式使用方法");
    });
    // 4. 优化Lambda
    showRun(sth->System.out.println("使用优化Lambda表达式使用方法"));
    }
    }

    其中Lambda和匿名内部类使用效果上几乎一致,但是底层原理则不同,其中匿名内部类执行后会有xxx$1.class文件,而Lambda表达式则不会有这个文件

Lambda延迟执行

日志文件:当方法参数等级为1,会打印信息,如果不为1,则什么也不做。

等级不为1时,方法也会先加载信息参数,假设该参数为拼接字符串,那么此时就会出现性能浪费,因为拼接字符串后却没有使用。

代码:

public class DemoLog {
public static void showMes(int level , String mes){
if (level == 1){
System.out.println(mes);
}
}
public static void main(String[] args) {
String a = "这里的";
String b = "山路";
String c = "十八弯";
showMes(1,a+b+c);
// 先拼接 后加载方法 判断不为1 不作为 性能浪费
showMes(2,a+b+c);
}
}

解决方法,使用Lambda表达式,其中Lambda表达式有个特点,就是延迟执行

public interface MessageBuilder {
public abstract String mesConcat();
}

public class DemoLogLambda {
public static void showMes(int level , MessageBuilder mb){
if (level == 1){
System.out.println(mb.mesConcat());
}
}

public static void main(String[] args) {
String a = "这里的";
String b = "山路";
String c = "十八弯";
// 如果等级为1 判断后使用接口中方法 拼接字符串
// 如果等级不为1 则什么也不做
// 提高了程序的效率,避免了性能浪费
showMes(1,()-> {return a+b+c;});
}
}

Lambda表达式作为参数

前提是函数式接口作为参数

[图片上传失败...(image-9a3910-1631103526996)]

public class DemoThreadLambda {
public static void startThread(Runnable run){
new Thread(run).start();
}
public static void main(String[] args) {
startThread(()->{
System.out.println("使用Lambda开启线程"+Thread.currentThread().getName());
});
// Lambda表达式为接口中实现的唯一抽象方法内容
startThread(()->System.out.println("使用Lambda开启线程"+Thread.currentThread().getName()));
}
}

使用Lambda作为返回值

前提是函数式接口作为返回值

[图片上传失败...(image-1b247-1631103526996)]

public class DemoCompLambda {
public static Comparator<String> getComparator(){
// 排序规则 降序
// 匿名内部类
/return new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
return o2.length() - o1.length();
}
};
/
// 由于Comparator为函数式接口 所以可以使用Lambda表达式
// return (String o1, String o2)->{return o2.length() - o1.length()};

// Lambda表达式优化
return (o1,o2)-> o2.length()-o1.length();
}

public static void main(String[] args) {
String[] arr = {"aaaa","b","cc","dddddddddddddd"};
System.out.println(Arrays.toString(arr));
Arrays.sort(arr,getComparator());
System.out.println(Arrays.toString(arr));
}
}

Lambda优化:在简化时 如果{}内只有一行代码 可以省略 { } 和 ; 如果有return 可以省略return

如果 () 参数列表中数据类型一样可以省略数据类型

常用函数式接口

常用的函数式接口在包 java.util.function包中

  • Supplier<T>

    java.util.function.Supplier

    接口 中有唯一方法 <T>get() 就是接口泛型为什么类型参数,方法返回值就是什么类型数据

    使用Supplier接口获取数组中最大值

    代码:

    public class DemoSupMax {
    public static int getMax(Supplier<Integer> sup){
    return sup.get();
    }
    // 使用Supplier获取数组最大值
    public static void main(String[] args) {
    int[] nums = {10,22,43,1,54,22,3};
    // 匿名内部类
    /*int max = getMax(new Supplier<Integer>() {
    @Override
    public Integer get() {
    int max = 0;
    for(int i:nums){
    if (i > max)
    max = i;
    }
    return max;
    }
    });
    */
    int res = getMax(() -> {
    int max = 0;
    for(int i:nums){
    if (i>max)
    max = i;
    }
    return max;
    });
    System.out.println("max: " +res);
    }
    }

  • Consumer<T>接口

    java.util.function.Consumer

    该接口中有一个唯一抽象方法,void accept(T t) 表示接口为什么数据类型泛型,accept就可以消费什么类型数据

    对于如何使用数据 自定义

    public class DemoConsumer {
    public static void revString(String name, Consumer<String> con){
    con.accept(name);
    }
    // 反转字符串
    public static void main(String[] args) {
    String name = "赵丽颖";
    // 链式编程
    // StringBuilder的反转字符串方法reverse
    revString(name,(String str)->System.out.println(new StringBuilder(name).reverse().toString()));
    }
    }

    默认方法:andThen

    源码:

default Consumer<T> andThen(Consumer<? super T> after) {
Objects.requireNonNull(after);
return (T t) -> { accept(t); after.accept(t); };
}
> <? super T> 下界通配符 只能使用T 或者T的父类

示例:

public class DemoConDefault {
 public static void useString(String str, Consumer<String> con1,Consumer<String> con2){
//        con1.accept(str); // 大写
//        con2.accept(str); // 小写
 // 谁写在前面 谁先执行(消费数据)
 con1.andThen(con2).accept(str);
 }
 public static void main(String[] args) {
 String s = "Hello";
 useString(s,(str)->{
 System.out.println(str.toUpperCase());
 },(str)->{
 System.out.println(str.toLowerCase());
 });
 }
}
练习:

[图片上传失败...(image-cec6ff-1631103526995)]

代码:

public class DemoConExam {
public static void ConcatInfo(String[] strings, Consumer<String> con1, Consumer<String> con2){
// con1 姓名 con2 性别
for(String s:strings){
con1.andThen(con2).accept(s);
}
}

public static void main(String[] args) {
String[] arr = {"灶门炭治郎,男","我妻善逸,男","香奈乎,女","蝴蝶忍,女"};
ConcatInfo(arr,(s)->{
String[] strs = s.split(",");
System.out.print("姓名:" +strs[0]+" ");
},(s)->{
String[] strs = s.split(",");
System.out.println("性别:" +strs[1]);
});
}
}

  • Predicate接口

    java.util.function.Predicate<T>接口

    主要用于判断,其中抽象方法test(T t) 判断T类型数据 boolean test (T t)

    public class DemoPredicate {
    public static boolean isLen(String string, Predicate<String> pre){
    return pre.test(string);
    }
    // 判断长度是否大于5
    public static void main(String[] args) {
    String s = "abc";
    // 因为是函数式接口所以可以使用Lambda表达式
    /boolean b = isLen(s,(String str)->{
    return str.length()>5;
    });
    /
    // 优化Lambda
    // 由于只有一个参数所以可以省略() 和 数据类型
    // 并且只有一行代码 所以可以省略{} ; 和 return
    boolean b = isLen(s,str->str.length()>5);
    System.out.println(b);
    }
    }

    默认方法:and

    逻辑 与 有假则假

    源码:

    default Predicate<T> and(Predicate<? super T> other) {
    Objects.requireNonNull(other);
    return (t) -> test(t) && other.test(t);
    }

    示例:

    public class DemoPredicateAnd {
    public static boolean booAnd(String s, Predicate<String> pre1,Predicate<String> pre2){
    // return pre1.test(s) && pre2.test(s);
    return pre1.and(pre2).test(s);
    }
    // 判断字符串 是否包含a 并且 长度大于3 必须都满足
    public static void main(String[] args) {
    String s = "dsjai";
    // 链式编程
    System.out.println(booAnd(s, str -> s.contains("a"), str -> str.length() > 3));
    }
    }

    默认方法:or

    逻辑或 有真则真

    源码:

    default Predicate<T> or(Predicate<? super T> other) {
    Objects.requireNonNull(other);
    return (t) -> test(t) || other.test(t);
    }

    示例:

    public class DemoPredicateAnd {
    public static boolean booAnd(String s, Predicate<String> pre1,Predicate<String> pre2){
    // return pre1.test(s) && pre2.test(s);
    // return pre1.and(pre2).test(s);
    return pre1.or(pre2).test(s);
    }
    // 判断字符串 是否包含a 或者 长度大于3 必须都满足
    public static void main(String[] args) {
    String s = "dsjai";
    // 链式编程
    System.out.println(booAnd(s, str -> s.contains("a"), str -> str.length() > 3));
    }
    }

    默认方法:negate

    逻辑非 取反

    源码:

    default Predicate<T> negate() {
    return (t) -> !test(t);
    }

    示例:

public class DemoPredicateNegate {
// 不大于5 返回true
public static boolean checkString(String s, Predicate<String> pre){
// return !pre.test(s);
return pre.negate().test(s);
}

 public static void main(String[] args) {
 String s = "acc";
 System.out.println(checkString(s, str -> str.length() > 5));
 }
}

练习:集合信息筛选

[图片上传失败...(image-4bb567-1631103526987)] 

代码:

public class DemoPredicateExam {
 public static List<String> chooseInfo(String[] arr, Predicate<String> pre1, Predicate<String> pre2){
 List<String> list = new ArrayList<>();
 for (String s:arr){
 if (pre1.and(pre2).test(s)){
 list.add(s);
 }
 }
 return list;
 }
 public static void main(String[] args) {
 String[] arr = {"迪丽热巴,女","古力娜扎,女","我妻善逸,男","赵丽颖,女"};
 // 这里切割出来的字符串与比较的字符串不是同一个地址值
 // 不能使用 == 要用equals
 List<String> infos = chooseInfo(arr, s -> s.split(",")[0].length()>=4
 , s -> "女".equals(s.split(",")[1]));
 System.out.println(infos);
 }
}
  • Function接口

    java.util.function.Function<T,R>

    抽象方法 R apply(T t) 将T类型数据转换为R数据类型

    源码:

    R apply(T t);

    代码:

    public class DemoFunction {
    public static void change(String s, Function<String, Integer> fun){
    Integer i = fun.apply(s);
    System.out.println(i + ": " + i.getClass());
    }
    // 将字符串类型转换为Integer
    public static void main(String[] args) {
    String s = "123";
    change(s,num->Integer.parseInt(num));
    }
    }
    默认方法andThen 组合转换数据

    案例:将字符串类型的数字转换为Integer 加 10

    再转换为字符串

    代码:

    public class DemoFuncAndThen {
    public static void method(String s, Function<String,Integer> fun1,Function<Integer,String> fun2){
    System.out.println(fun1.andThen(fun2).apply(s)+": "+fun1.andThen(fun2).apply(s).getClass());
    }

    public static void main(String[] args) {
    String s = "123";
    method(s,num->Integer.parseInt(num)+10,num->Integer.toString(num));
    }
    }

    Lambda表达式中 () 只有一个参数时 可省略 数据类型和()

    {} 中只有一行代码 且有 return 可省略 {} ;和return

    [图片上传失败...(image-1a0449-1631103526986)]

    代码:

    public class DemoFuncExam {
    public static void exam(String s, Function<String,String> fun1,Function<String,Integer> fun2,Function<Integer,Integer> fun3){

    Integer i = fun1.andThen(fun2).andThen(fun3).apply(s);
    System.out.println(i+": "+i.getClass());
    }
    public static void main(String[] args) {
    String s = "迪丽热巴,19";
    exam(s,str->str.split(",")[1],str->Integer.parseInt(str),num->num+100);
    }
    }

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

友情链接更多精彩内容