【Java8新特性】函数式接口

本文围绕Java函数式接口展开,介绍了@FunctionInterface注解及函数式接口的调用。阐述了函数式编程中Lambda的延迟加载技术和表达式的使用,还详细讲解了常用函数式接口,如Supplier、Consumer、Predicate、Function和Operator接口及其默认方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在这里插入图片描述

1. 介绍

Java中的函数式接口指的是:有且仅有一个抽象方法的接口。(通常接口上方都会有@FunctionInterface注解,用于在编译期发现错误)

Java函数式接口的体现就是Lambda表达式,所有函数式接口都是适用于Lambda表达式使用的接口。

1.1 @FunctionInterface注解

@FunctionalInterface // 表明该类为函数式接口
public interface DoubleSupplier {
    double getAsDouble();
}

只要在类上加上@FunctionInterface注解,则告诉了编译器该类为函数式接口,只要不符合函数接口的只有一个抽象方法的规范,就会报错。

注意:即使不加@FunctionInterface接口,只有一个抽象方法的类,也能算是函数是接口,只是编译器不知道。

1.2 函数式接口的调用

public class TestFunInterface2 {
    //需求:使用Consumer接口作为方法的参数,对字符串实现翻转
    public static void revString(String str, Consumer<String> consumer){
        consumer.accept(str);
    }

    public static void main(String[] args) {
        revString("zhangsan",(str)->{
            StringBuilder reverseStr = new StringBuilder(str).reverse();
            System.out.println("翻转后的字符串:"+reverseStr);
        });
    }
}

2. 函数式编程

2.1 Lambda的延迟加载技术

通过log4j的日志打印功能举例:

public class Demo01Logger {
    private static void log(int level, String msg) {
        if (level == 1) {
            System.out.println(msg);
        }
     }
    public static void main(String[] args) {
        String msgA = "Hello";
        String msgB = "World";
        String msgC = "Java";
        log(1, msgA + msgB + msgC);// 无论是否满足log级别为1,都会执行字符串拼接,造成性能浪费。
    }
}

经过Lambda表达式的优化后:只有满足日志级别为2,才能进入lambda表达式,进行字符拼接。

@FunctionalInterface
public interface MessageBuilder {
    String buildMessage();
}
public class Demo02LoggerLambda {
    private static void log(int level, MessageBuilder builder) {
        if (level == 1) {
             // 实际上利用内部类 延迟的原理,代码不相关 无需进入到启动代理执行
             System.out.println(builder.buildMessage());
           
        }
    }
    public static void main(String[] args) {
        String msgA = "Hello";
        String msgB = "World";
        String msgC = "Java";
       log(2,()->{
                System.out.println("lambda 是否执行了");
                return msgA + msgB + msgC;
        });
    }
}

2.2 Lambda表达式的使用

  1. Lambda表达式作为参数
public class Runnable {
    private static void startThread(Runnable task) {
		new Thread(task).start();
    }
    public static void main(String[] args) {
		startThread(()> System.out.println("线程任务执行!"));
    }
}
  1. Lambda表达式作为返回值
public class lambda_Comparator {
    private static Comparator<String> newComparator1(){
        return new Comparator<String>() {
            @Override
            public int compare(String a, String b) {
                return b.length()-a.length();    
            }
        };
    }
    public static void main(String[] args) {
        String[] array={"abc","ab","abcd"};
        Arrays.sort(array, newComparator1()); // 方式一:复杂写法
        Arrays.sort(array,(a,b)->b.length()-a.length());//更简单的方式
        System.out.println(Arrays.toString(array));
    }
}

3. 常用的函数式接口

3.1 Supplier生产型接口

java.util.function.Supplier<T> 接口是生产一个数据,其数据类型由泛型T来定。

Supplier 接口仅包含一个无参的方法: T get() 。用来获取一个泛型参数指定类型的对象数据。

public class Test_Supplier {
    private static String test_Supplier(Supplier<String> suply) {
        return suply.get(); //供应者接口
    }
    public static void main(String[] args) {
         // Lambda表达式写法:产生的数据作为 sout 作为输出
         System.out.println(test_Supplier(()->"产生数据"));
         
         // 匿名内部类写法:
         System.out.println(String.valueOf(new Supplier<String>() {
             @Override
            public String get() {
                return "产生数据";
            }
        }));
    }
}

3.2 Consumer消费型接口

java.util.function.Consumer<T> 接口是消费一个数据,其数据类型由泛型决定。

Consumer 接口中包含抽象方法 void accept(T t) ,表示消费一个指定泛型的数据。基本使用如:

public class Test_Comsumer {
    public static void generateX(Consumer<String> consumer) {
        consumer.accept("hello consumer");
    }
    public static void main(String[] args) {
        generateX(s->System.out.println(s));
    }
}
默认方法:andThen

将两个Consumer组合,先消费一条数据,随后再消费一条数据。

default Consumer<T> andThen(Consumer<? super T> after) {
    Objects.requireNonNull(after);
    return (T t)> { accept(t); after.accept(t); }; 
    //1:  返回值为Consumer 那么需要 ()-> 表示函数式接口
    //2:  accept(t);为生产一个数据供应给 (T t)中的t
    //3:  after.accept(t);为利用这个t再次生成新的函数式接口 实现类始于builder的设计模式
}

例如:

public class TestFunInterface3 {
    public static void method(String s, Consumer<String> con1,Consumer<String> con2){
        //con1.accept(s);
        //con2.accept(s);
        con1.andThen(con2).accept(s); //谁在前面谁先消费
    }

    public static void main(String[] args) {
       method("zhangsan",(str)->{
           System.out.println(str.toUpperCase());
       },(str)->{
           System.out.println(str.toLowerCase());
       });
    }
}

3.3 Predicate条件判断接口

java.util.function.Predicate<T> 用于对某种类型的数据进行判断,并得到一个boolean值结果。(即是生产者,又是消费者)

抽象方法: boolean test(T t) 用于条件判断的场景:

默认方法: and(与)、or(或)、nagte(取反)

既然是条件判断,就会存在与、或、非三种常见的逻辑关系。

其中将两个 Predicate 条件使用“与”逻辑连接起来实现“并且”的效果时,类始于 Consumer接口 andThen()函数 其他三个雷同

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

静态方法: isEquals

static <T> Predicate<T> isEqual(Object targetRef) {
	return (null == targetRef)
	         ? Objects::isNull
	         : object -> targetRef.equals(object);
}

举例:

public class Use_Predicate {
    // 判断字符串是否存在o,即是生产者 又是消费者接口
    private static void method_test(Predicate<String> predicate) {
         boolean b = predicate.test("OOM SOF");
         System.out.println(b);
    }
    // 判断字符串是否同时存在o和h 
    private static void method_and(Predicate<String> predicate1,Predicate<String> predicate2) {
        boolean b = predicate1.and(predicate2).test("OOM SOF");
        System.out.println(b);
    }
    // 判断字符串是否存在o或者h 
    private static void method_or(Predicate<String> predicate1,Predicate<String> predicate2) {
        boolean b = predicate1.or(predicate2).test("OOM SOF");
        System.out.println(b);
    }
    // 判断字符串是否存在o,结果取反
    private static void method_negate(Predicate<String> predicate) {
         boolean b = predicate.negate().test("OOM SOF");
         System.out.println(b);
    }
    public static void main(String[] args) {
        method_test((s)->s.contains("O"));
        method_and(s->s.contains("O"), s->s.contains("h"));
        method_or(s->s.contains("O"), s->s.contains("h"));
        method_negate(s->s.contains("O"));
    }
}

3.4 Function普通函数接口

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

简单来说就是:将 T 转为 R 返回

	// 将数字转换为String类型
	private static void numberToString(Function<Number, String> function) {
		String apply = function.apply(12);
		System.out.println("转换结果:"+apply);
	}
	public static void main(String[] args) {
		numberToString((s)->String.valueOf(s));
	}
默认方法:andThen compose()

区别: 执行顺序不同,andThen是调用者先执行,传入的function后执行;compose是传入的function先执行,调用者后执行。

default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
	Objects.requireNonNull(after);
	// 先执行调用者,再执行after的apply方法
	return (T t) -> after.apply(apply(t));
}  
// 这里的V 一个是作为输入值 一个是作为输出值  按照调用的顺序的不同 对于 T V 做输入 输出的顺序也不同 注意看
default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
	Objects.requireNonNull(before);
	// 先执行before的apply方法,后执行调用者apply方法
	return (V v) -> apply(before.apply(v));
}

注意调用的先后顺序:

// 静态方法
private static void method_andThen(Function<Integer, Integer> f1, Function<Integer, Integer> f2) {
    // andThen:先执行f1,再执行f2
    Integer apply = f1.andThen(f2).apply(2);
    System.out.println(apply);
}

private static void method_compose(Function<Integer, Integer> f1, Function<Integer, Integer> f2) {
    // compose:先执行f2,再执行f1
    Integer apply = f1.compose(f2).apply(2);
    System.out.println(apply);
}

public static void main(String[] args) {
    method_andThen(s -> s + 1, s -> s = s * 2);//6
    method_compose(s -> s + 1, s -> s = s * s);//5
}

3.5 Operator 同类型相互转换接口

个人理解: 例如对字符串操作,返回字符串类型。像Stream流中的map方法,但是限定了返回值和参数相同。

BinaryOperator<Integer>andthen() 方法不支持两个函数链接操作 也就是不需要再次 BinaryOperator<Integer> 因为源代码规定不允许使用两次输入。

public static void main(String[] args) {
    // 单个同类型操作
    UnaryOperator<String> u_str=(s)->s.split(" ")[0];
    UnaryOperator<String> u_str1=(s)->s.concat(" ok");

    String apply = u_str.andThen(u_str1).apply("lambda Ok");
    String apply1 = u_str.compose(u_str1).apply("lambda Ok");
    System.out.println(apply);
    System.out.println(apply1);

    // 两个同类型操作
    BinaryOperator<Integer> way_add=(k, v)->k+v;
    BinaryOperator<Integer> way_mul=(k,v)->k*v;
    // 注意不能连写!
    Integer res1 = way_add.apply(1,2);
    Integer res2 = way_mul.apply(1,2);
    System.out.println(res1);
    System.out.println(res2);
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值