Java8 函数式接口原理及用法

Java8相比之前的Java新增了几个重要的功能:Lambda表达式、函数式接口、Stream流。下面介绍一下Java中常用的函数式接口

一、什么是函数式接口?

函数式接口是指只有一个抽象方法的接口,它有个专门的注解@FunctionalInterface,该注解主要特点有:

1、该注解只能标记在"有且仅有一个抽象方法"的接口上,表示函数式接口。
2、JDK8接口中的静态方法和默认方法,都不算是抽象方法。
3、接口默认继承 java.lang.Object,所以如果接口显示声明覆盖了Object中的方法,那么也不算抽象方法。
4、允许java.lang.Object中的public方法
5、该注解不是必须的,如果一个接口符合"函数式编程"定义,那么加不加该注解都没有影响。加上该注解能够 更好地让编译器进行检查,
如果编写的不是函数式接口,但是加上了@FunctionalInterface 那么编译器会报错。

二、使用方法

 定义一个函数式接口,说明它的用法

/**
 * 函数式接口注解
 */
@FunctionalInterface
public interface PersonInterface { 
    /**
     * 1、仅有一个抽象方法
     */
     void say();
     
    /**
     * 2、java.lang.Object中的方法不算
     */
    @Override
     boolean equals(Object var1);
    
    /**
     * 3、java8 接口才可以有默认的方法实现 前提是方法名称必须使用default关键字修饰
     */
     default void defaultMethod() {
        System.out.println("haha");
    }
    
    /**
     * 4、静态方法
     */
     static void staticMethod() {
    }
}

三、为什么只能有一个抽象方法?

函数式接口主要是为lambda表达式服务的,只有一个抽象方法可以使代码更简洁。下面写代码说明

  public static void main(String[] args) {
       //上面的接口 通过Lambda表达式重新 say方法 
        PersonInterface inter = () -> System.out.println("我说什么好呢?");
        inter.say();
        //控制台输出: 我说什么好呢?
    }

通过 函数式接口 + Lambda表达式 让代码看去变的简洁,而这里的关键点在于:

()->{}这样的方法觉得代表对say方法的重写,如果有多个方法编译器就 不知道重写哪个方法了,这就是只有一个抽象方法的原因。

四、Java8中的函数式接口

Java8中带有的函数式接口主要有4个:FunctionConsumerPredicateSupplier,它们的特点如下:

1、Supplier:

   供给类型,没有入参有返回值,主要用来提前定义可能返回的一个指定类型结果,等需要调用的时候再获取结果。代码如下:

@FunctionalInterface
public interface Supplier<T> {

    /**
     * 只有这一个抽象类
     */
    T get();
}

Supplier在JDK8中Optional对象有使用到

Optional.orElseGet(Supplier<? extends T>) //当this对象为null,就通过传入supplier创建一个T返回。

   使用的栗子

 public static void main(String[] args) {
        Person son = null;
        //先判断son是否为null,如果为不为null则返回当前对象,如果为null则返回新创建的对象
        BrandDTO optional = Optional.ofNullable(son).orElseGet(() -> new Person());

    }

2、Consumer

消费类型,有输入参数没有返回值。主要是对对象进行处理,代码如下:

@FunctionalInterface
public interface Consumer<T> {

    /**
     * 抽象方法:传入一个指定泛型的参数,无返回值
     */
    void accept(T t);

    /**
     * 如同方法名字一样andThen,类似一种相加的功能(下面会举例说明)
     */
    default Consumer<T> andThen(Consumer<? super T> after) {
        Objects.requireNonNull(after);
        return (T t) -> { accept(t); after.accept(t); };
    }
}

  使用的示例如下:

 public static void main(String[] args) {
        testConsumer();
        testAndThen();
    }
    /**
     * 一个简单的平方计算
     */
    public static void testConsumer() {
        //设置好Consumer实现方法
        Consumer<Integer> square = x -> System.out.println("平方计算 : " + x * x);
        //传入值
        square.accept(2);
    }
    /**
     * 定义3个Consumer并按顺序进行调用andThen方法
     */
    public static void testAndThen() {
        //当前值
        Consumer<Integer> consumer1 = x -> System.out.println("当前值 : " + x);
        //相加
        Consumer<Integer> consumer2 = x -> { System.out.println("相加 : " + (x + x)); };
        //相乘
        Consumer<Integer> consumer3 = x -> System.out.println("相乘 : " + x * x);
        //andThen拼接
        consumer1.andThen(consumer2).andThen(consumer3).accept(1);
    }

Java8中的Stream中的函数peek和forEach接收的参数就是Consumer。

public interface Iterable<T> {
    //forEach方法传入的就是Consumer
    default void forEach(Consumer<? super T> action) {
        Objects.requireNonNull(action);
        for (T t : this) {
            action.accept(t);
        }
    }
}

3、Function

  既有入参、又有返回值。这种函数式接口主要用来输入一个值、得到处理后的值,代码如下:

 @FunctionalInterface
    public interface Function<T, R> {
        
        /**
         * 抽象方法: 根据一个数据类型T加工得到一个数据类型R
         */
        R apply(T t);

        /**
         * 组合函数,调用当前function之前调用
         */
        default <V> java.util.function.Function<V, R> compose(java.util.function.Function<? super V, ? extends T> before) {
            Objects.requireNonNull(before);
            return (V v) -> apply(before.apply(v));
        }

        /**
         * 组合函数,调用当前function之后调用
         */
        default <V> java.util.function.Function<T, V> andThen(java.util.function.Function<? super R, ? extends V> after) {
            Objects.requireNonNull(after);
            return (T t) -> after.apply(apply(t));
        }

        /**
         *  静态方法,返回与原函数参数一致的结果。x=y
         */
        static <T> java.util.function.Function<T, T> identity() {
            return t -> t;
        }
    }

使用的栗子

public static void main(String[] args) {
        applyTest();
        andThenTest();
        composeTest();
        test();
    }

    /**
     * 1、apply 示例
     */
    private static void applyTest() {
        //示例1:定义一个funciton,实现将String转换为Integer
        Function<String, Integer> function = x -> Integer.parseInt(x);
        Integer a = function.apply("100");
        System.out.println(a.getClass());
        // 结果:class java.lang.Integer
    }

    /**
     * 2、andThen 示例
     */
    private static void andThenTest() {
        //示例2:使用andThen() 实现一个函数 y=10x + 10;
        //先执行 10 * x
        Function<Integer, Integer> function2 = x -> 10 * x;
        //通过andThen在执行 这里的x就等于上面的10 * x的值
        function2 = function2.andThen(x -> x + 10);
        System.out.println(function2.apply(2));
        //结果:30

    }

    /**
     * 3、compose 示例
     */
    private static void composeTest() {
        //示例3:使用compose() 实现一个函数 y=(10+x)2;
        Function<Integer, Integer> function3 = x -> x * 2;
        //先执行 x+10 在执行(x+10)*2顺序与上面相反
        function3 = function3.compose(x -> x + 10);
        System.out.println(function3.apply(3));
        //结果:26
    }

    /**
     * 4、综合示例
     */
    private static void test() {

//示例4:使用使用compose()、andThen()实现一个函数 y=(10+x)*2+10;
        //执行第二步
        Function<Integer, Integer> function4 = x -> x * 2;
        //执行第一步
        function4 = function4.compose(x -> x + 10);
        //执行第三步
        function4 = function4.andThen(x -> x + 10);
        System.out.println(function4.apply(3));
       //结果:36

    }

Java8的Stream流中的map的参数就是Function,HashMap中的函数computeIfAbsent的参数也是Fucntion。代码如下:

1、V HashMap.computeIfAbsent(K , Function<K, V>) // 简化代码,如果指定的键尚未与值关联或与null关联,使用函数返回值替换。
2、<R> Stream<R> map(Function<? super T, ? extends R> mapper); // 转换流

4、Predicate

Predicate接口主要用来判断一个参数是否符合要求。代码如下:

@FunctionalInterface
public interface Predicate<T> {
    /**
     * 具体过滤操作 需要被子类实现.
     * 用来处理参数T是否满足要求,可以理解为 条件A
     */
    boolean test(T t);
    /**
     * 调用当前Predicate的test方法之后再去调用other的test方法,相当于进行两次判断
     * 可理解为 条件A && 条件B
     */
    default Predicate<T> and(Predicate<? super T> other) {
        Objects.requireNonNull(other);
        return (t) -> test(t) && other.test(t);
    }
    /**
     * 对当前判断进行"!"操作,即取非操作,可理解为 ! 条件A
     */
    default Predicate<T> negate() {
        return (t) -> !test(t);
    }
    /**
     * 对当前判断进行"||"操作,即取或操作,可以理解为 条件A ||条件B
     */
    default Predicate<T> or(Predicate<? super T> other) {
        Objects.requireNonNull(other);
        return (t) -> test(t) || other.test(t);
    }

    /**
     * 对当前操作进行"="操作,即取等操作,可以理解为 A == B
     */
    static <T> Predicate<T> isEqual(Object targetRef) {
        return (null == targetRef)
                ? Objects::isNull
                : object -> targetRef.equals(object);
    }
}

常用的栗子

public static void main(String[] args) {
        /**
         * 1、判断数字是否大于7
         */
        //设置一个大于7的过滤条件
        Predicate<Integer> predicate = x -> x > 7;
        System.out.println(predicate.test(10)); //输出 true
        System.out.println(predicate.test(6));  //输出 fasle
         /**
          * 2、大于7并且
          */
        //在上面大于7的条件下,添加是偶数的条件
        predicate = predicate.and(x -> x % 2 == 0);
        System.out.println(predicate.test(6));  //输出 fasle
        System.out.println(predicate.test(12)); //输出 true
        System.out.println(predicate.test(13)); //输出 fasle
        /**
         * 3、add or 简化写法
         */
        predicate = x -> x > 5 && x < 9;
        System.out.println(predicate.test(10)); //输出 false
        System.out.println(predicate.test(6));  //输出 true
    }

Java8中的Stream的fitler的参数就是一种predicate。

五、总结

Java8中新增的函数式接口是lambda表达式、Stream流的基础,有了它编写的代码更加简洁。我们平时的代码开发也可以多用函数式来编写代码,既可以提高开发效率又可以使代码更简洁。

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值