JDK1.8新特性:函数式接口

Posted 流楚丶格念

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JDK1.8新特性:函数式接口相关的知识,希望对你有一定的参考价值。

文章目录

函数式接口

函数式接口概念

函数式接口(Functional Interface)就是一个有且仅有一个抽象方法,但是可以有多个非抽象方法的接口。

函数式接口可以被隐式转换为 lambda 表达式。
Lambda 表达式和方法引用(实际上也可认为是Lambda表达式)上。

@FunctionalInterface

我们常用的Runnable接口就是个典型的函数式接口,我们可以看到它有且仅有一个抽象方法run:

@FunctionalInterface
public interface Runnable 
    /**
     * When an object implementing interface <code>Runnable</code> is used
     * to create a thread, starting the thread causes the object's
     * <code>run</code> method to be called in that separately executing
     * thread.
     * <p>
     * The general contract of the method <code>run</code> is that it may
     * take any action whatsoever.
     *
     * @see     java.lang.Thread#run()
     */
    public abstract void run();

并且可以看到一个注解@FunctionalInterface,这个注解的作用是强制你的接口只有一个抽象方法。

如果有多个话直接会报错,如图:

idea错误提示:

翻译下:

函数式接口使用方式

我们直接上代码,首先定义一个函数式接口

package com.yyl.lambda;

@FunctionalInterface
public interface SingleAbstraMethodInterface 

    public abstract void singleMethod();

我们定一个test类,封装一个方法,将SingleAbstraMethodInterface当做参数传入方法,并打印一句话

package com.yyl.lambda;

public class TestDemo 
    public void testMethod(SingleAbstraMethodInterface single)
        System.out.println("即将执行函数式接口外部定义方法");
        single.singleMethod();
    
    public static void main(String[] args) 
        TestDemo test = new TestDemo();
        test.testMethod(new SingleAbstraMethodInterface() 
            @Override
            public void singleMethod() 
                System.out.println("执行函数式接口定义方法");
            
        );
    


运行结果如下:

是不是和我们预期结果一样。这个过程是不是有的同学已经发现,怎么这么像jdk里面的一些接口的使用,比如常用的Runnable接口。我们来看看代码。

public static void main(String[] args) 
    new Thread(new Runnable() 
        @Override
        public void run() 
            System.out.println("run方法执行了");
        
    ).start();


再看下Runnable接口源码,是不是一样😜😜😜

@FunctionalInterface
public interface Runnable 
    public abstract void run();

这时,有的同学会说,我用Runnable接口可不是这么用的,我的写法比你优雅,我是这么写的。
没错函数式接口,函数式编程,我们可以使用lambda表达式的写法,可以让代码更优雅,具体可以参照我的前一篇帖子。

public static void main(String[] args) 
    new Thread(()-> 
        System.out.println("run方法执行了");
    ).start();

常用函数式接口

JDK提供了大量常用的函数式接口以丰富Lambda的典型使用场景,它们主要在 java.util.function 包中被提供。

其实,jdk中给我们提供了很多的函数式接口,我们平时都会用到,只不过大家没有注意到而已,这里我结合实际代码讲解几个常用的函数式接口。想想大家平时常常用到stream流的各种方法来处理list。我看去stream类中看一下它提供的方法。

可以看到有几个出镜率较高的函数式接口:

  • Supplier
  • Comsumer
  • Predicate
  • Function

比如我们使用filter,需要实现的就是Predicate这个函数式接口:

List<Man> list = new LinkedList<>();
// 匿名内部类写法
list.stream().filter(new Predicate<Man>() 
    @Override
    public boolean test(Man man) 
        if (man.getName().equals("如梦"))
            return true;
        
        return false;
    
);
// lanmda写法
list.stream().filter((man)->
    if (man.getName().equals("如梦"))
        return true;
    
    return false;
);

Supplier

Supplier 接口被称之为生产型接口,指定接口的泛型是什么类型,那么接口中的 get 方法就会产生什么类型的数据

我们追踪一下源码:

@FunctionalInterface
public interface Supplier<T> 
    /**
     * Gets a result.
     * @return a result
     */
    T get();

Supplier接口的get方法没有入参,返回一个泛型T对象。我们来看几个使用的实例。先写一个实验对象Man,我们定义一个方法,创建对象,我们使用lambda的方式来调用并创建对象。

完整示例如下:

package com.yyl.lambda;

import java.util.LinkedList;
import java.util.List;
import java.util.Optional;
import java.util.function.Predicate;
import java.util.function.Supplier;

class Man 
    private String name;
    private int age;

    public Man() 
    

    public String getName() 
        return name;
    

    public void setName(String name) 
        this.name = name;
    

    public int getAge() 
        return age;
    

    public void setAge(int age) 
        this.age = age;
    

    public Man(String name, int age) 
        this.name = name;
        this.age = age;
    

    public static Man create(final Supplier<Man> supplier) 
        return supplier.get();
    


public class SupplierDemo 

    public static void main(String[] args) 
        Man man1 = Man.create(Man::new);
        Man man2 = Man.create(() -> new Man("玉如梦", 22));
        Man man = null;

        //orElseGet
        Man man3 = Optional.ofNullable(man).orElseGet(Man::new);

        System.out.println(man1);
        System.out.println(man2);
        System.out.println(man);
        System.out.println(man3);
    

运行结果如下:

Comsumer

java.util.function.Consumer 接口则正好与Supplier接口相反,它不是生产一个数据,而是消费一个数据, 其数据类型由泛型决定

源码如下:

@FunctionalInterface
public interface Consumer<T> 

    /**
     * Performs this operation on the given argument.
     *
     * @param t the input argument
     */
    void accept(T t);

    default Consumer<T> andThen(Consumer<? super T> after) 
        Objects.requireNonNull(after);
        return (T t) ->  accept(t); after.accept(t); ;
    


可以看到accept方法接受一个对象,没有返回值。

那么我们来实战,首先使用Lambda表达式声明一个Supplier的实例,它是用来创建Man实例;再使用Lambda表达式声明一个Consumer的实例,它是用于打印出Man实例的toString信息;最后Consumer消费了Supplier生产的Man。我们常用的forEach方法入参也是Consumer。

代码如下:

Supplier<Man> supplier = ()-> new Man("lucy",33);
Consumer<Man> consumer = (Man g)->
    System.out.println(g.toString());
;
consumer.accept(supplier.get());

Predicate

需要对某种类型的数据进行判断,从而得到一个boolean值结果。这时可以使用 java.util.function.Predicate 接口

源码如下:

@FunctionalInterface
public interface Predicate<T> 

    /**
     * Evaluates this predicate on the given argument.
     *
     * @param t the input argument
     * @return @code true if the input argument matches the predicate,
     * otherwise @code false
     */
    boolean test(T t);

可以看到test方法接受一个对象,返回boolean类型,这个函数显然是用来判断真假。那么我们用这个接口来构造一个判断Man条件的示例,还有我们常用的stream流中,filter的入参也是Predicate,代码如下

代码如下:

Supplier<Man> supplier = ()-> new Man("lucy",33);
Predicate<Man> man36 = (Man g)-> Objects.equals(g.getAge(),36);
Predicate<Man> man33 = (Man g)-> Objects.equals(g.getAge(),33);
boolean test33 = man33.test(supplier.get());
boolean test36 = man36.test(supplier.get());
System.out.println(supplier.get().getName() +"是否为[36] :"+test36);
System.out.println(supplier.get().getName() +"是否为[33] :"+test33);

运行结果如下:

Function

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

源码如下:

@FunctionalInterface
public interface Function<T, R> 
    /**
     * Applies this function to the given argument.
     *
     * @param t the function argument
     * @return the function result
     */
    R apply(T t);

这个看到apply接口接收一个泛型为T的入参,返回一个泛型为R的返回值,所以它的用途和Supplier还是略有区别。还是一样我们举个栗子用代码来说明:

Supplier<Man> supplier = ()-> new Man("lucy",33);
Function<Man,String> manMark = (Man man)-> man.getName()+"的年龄是"+man.getAge();
System.out.println(manMark.apply(supplier.get()));

运行结果:

常用函数式接口相关扩展接口

这里我列举一些函数式接口扩展接口,大家自己研究,我没咋研究

Supplier相关拓展接口

接口名称方法名称方法签名
Supplierget() -> T
BooleanSuppliergetAsBoolean() -> boolean
DoubleSuppliergetAsDouble() -> double
IntSuppliergetAsInt() -> int
LongSuppliergetAsLong() -> long

Comsumer相关拓展接口

接口名称方法名称方法签名
Consumeraccept(T) -> void
DoubleConsumeraccept(double) -> void
IntConsumeraccept(int) -> void
LongConsumeraccept(long) -> void
ObjDoubleConsumeraccept(T, double) -> vo
ObjIntConsumeraccept(T, int) -> void
ObjLongConsumeraccept(T, long) -> void

Predicate相关拓展接口

接口名称方法名称方法签名
Predicatetest(T) -> boolean
BiPredicatetest(T, U) -> boolean
DoublePredicatetest(double) -> bool
IntPredicatetest(int) -> boolean
LongPredicatetest(long) -> boolean

Function相关的接口

接口名称方法名称方法签名
Functionapply(T) -> R
BiFunctionapply(T, U) -> R
DoubleFunctionapply(double) -> R
DoubleToIntFunctionapplyAsInt(double) -> int
DoubleToLongFunctionapplyAsLong(double) -> long
IntFunctionapply(int) -> R
IntToDoubleFunctionapplyAsDouble(int) -> double
IntToLongFunctionapplyAsLong(int) -> long
LongFunctionapply(long) -> R
LongToDoubleFunctionapplyAsDouble(long) -> double
LongToIntFunctionapplyAsInt(long) -> int
ToDoubleFunctionapplyAsDouble(T) -> double
ToDoubleBiFunctionapplyAsDouble(T, U) -> double
ToIntFunctionapplyAsInt(T) -> int
ToIntBiFunctionapplyAsInt(T, U) -> int
ToLongFunctionapplyAsLong(T) -> long
ToLongBiFunctionapplyAsLong(T, U) -> long

以上是关于JDK1.8新特性:函数式接口的主要内容,如果未能解决你的问题,请参考以下文章

快来看看!!JDK1.8新特性之函数式接口

JDK1.8新特性之--函数式接口

Jdk1.8新特性之Lambda表达式

jdk1.8新特性(四大函数式接口与Stream流式计算)

图解jdk1.8新特性

JDK1.8特性(上):函数式接口