java8新特性之函数式接口

11/24/2019 Lambda

# 函数式接口

上篇 java8新特性之Lambda表达式 (opens new window),说了以下java8 Lambda的基本语法,再来说一下函数式接口。你会发现每次运用时,都会有一个接口类型来接收,它有一个专门的名字叫函数式接口,但是并不是每个接口都可以叫函数式接口,必须满足有且只有一个抽象方法的条件,并且还有以下的特点。

# 函数式接口定义

  • 带有@FunctionInterface注解的接口是函数式接口,编译器会对其做编译检查,不符合条件会报错
  • 函数式接口允许默认方法存在 default-method
  • 函数式接口允许静态方法存在 static-method
  • 函数式接口允许对Object方法的重写例如 toString() equals()

线程接口Runnable就是一个典型的函数式接口

package java.lang;
/**
 * The <code>Runnable</code> interface should be implemented by any
 * class whose instances are intended to be executed by a thread. The
 * class must define a method of no arguments called <code>run</code>.
 * <p>
 * This interface is designed to provide a common protocol for objects that
 * wish to execute code while they are active. For example,
 * <code>Runnable</code> is implemented by class <code>Thread</code>.
 * Being active simply means that a thread has been started and has not
 * yet been stopped.
 * <p>
 * In addition, <code>Runnable</code> provides the means for a class to be
 * active while not subclassing <code>Thread</code>. A class that implements
 * <code>Runnable</code> can run without subclassing <code>Thread</code>
 * by instantiating a <code>Thread</code> instance and passing itself in
 * as the target.  In most cases, the <code>Runnable</code> interface should
 * be used if you are only planning to override the <code>run()</code>
 * method and no other <code>Thread</code> methods.
 * This is important because classes should not be subclassed
 * unless the programmer intends on modifying or enhancing the fundamental
 * behavior of the class.
 *
 * @author  Arthur van Hoff
 * @see     java.lang.Thread
 * @see     java.util.concurrent.Callable
 * @since   JDK1.0
 */
@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();
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44

我们也可以自己定义函数式接口,定义一个接口,使用lambda表达式传递接口并且调用

/**
 * 函数式接口
 */
@FunctionalInterface //只是为了编译器检查 可以不写
public interface MyInterface {
    // 有且只有一个抽象方法
    void sayHello(String name);
    //1.可以重写 Object的toString方法
    @Override
    String toString();
    // 也可以重写 Object的equals方法,还可以重写其他方法,这里只举个例子
    @Override
    boolean equals(Object object);
    //2 可以有默认方法
    default void sayHi(String name) {
        System.out.println("hi :" + name);
    }
    //3 可以有静态方法
    static boolean isEqual(Object obj) {
    return (null == obj)
            ? true
            : false;
    }
}

--------------------------------------

@Test
public void test() {
    // 调用 需要一个String参数 然后打印输出
    say("black猫", (name) -> System.out.println(name + ": hello"));
}
/**
  * 声明一个 接口传递方法
  * @param name 接口需要的参数
  * @param myInterface 是函数式类型接口 
  */
public void say(String name, MyInterface<String> myInterface) {
    myInterface.sayHello(name);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40

其实我们不用自己定义这些函数式接口,java8给我们内置好了一些接口如下:

  • 消费型接口 Consumer<T>:void accept(T t);
  • 供给型接口 Supplier<T>:T get();
  • 函数型接口 Function<T,R>:R apply(T t);
  • 断言型接口 Predicate<T>:boolean test(T t);

# 消费型接口Consumer

Consumer接口除了一个抽象方法accept(T t)还有一个默认实现的andThen(Consumer consumer),用来在前一个消费接口调用完accept()后使用,可以理解为一个连续性操作。

package java.util.function;
import java.util.Objects;
/**
 * Represents an operation that accepts a single input argument and returns no
 * result. Unlike most other functional interfaces, {@code Consumer} is expected
 * to operate via side-effects.
 *
 * <p>This is a <a href="package-summary.html">functional interface</a>
 * whose functional method is {@link #accept(Object)}.
 *
 * @param <T> the type of the input to the operation
 *
 * @since 1.8
 */
@FunctionalInterface
public interface Consumer<T> {
    /**
     * Performs this operation on the given argument.
     *
     * @param t the input argument
     */
    void accept(T t);
    /**
     * Returns a composed {@code Consumer} that performs, in sequence, this
     * operation followed by the {@code after} operation. If performing either
     * operation throws an exception, it is relayed to the caller of the
     * composed operation.  If performing this operation throws an exception,
     * the {@code after} operation will not be performed.
     *
     * @param after the operation to perform after this operation
     * @return a composed {@code Consumer} that performs in sequence this
     * operation followed by the {@code after} operation
     * @throws NullPointerException if {@code after} is null
     */
    default Consumer<T> andThen(Consumer<? super T> after) {
        Objects.requireNonNull(after);
        return (T t) -> { accept(t); 
        after.accept(t);
         };
    }
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42

# 消费型接口Consumer的三个例子

1.Consumer接口的accept消费一个参数(反转字符串)

@Test
public void  test01(){
  testConsumer01("hello",(msg)->System.out.println(new StringBuffer(msg).reverse().toString());
}

//调用 accept
public void testConsumer01(String str,Consumer<String> consumer){
    consumer.accept(str);
}
1
2
3
4
5
6
7
8
9

2.两个消费接口 调用accept,实现一个输出字符串大写,一个输出字符串输出小写

@Test
public void test02(){
    testConsumer02("Hello Lambda",(str)->{
        System.out.println(str.toUpperCase());
    },(str)->{
        System.out.println(str.toLowerCase());
    }
    );
}
//两个消费接口 调用 accept
public void testConsumer02(String str,Consumer<String> consumer1,Consumer<String> consumer2){
    consumer1.accept(str);
    consumer2.accept(str);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

3.两个消费接口的accept和andThen组合操作字符

@Test
public void test03() {
    String[] names = {"张山,男", "李诗,女", "王武,男", "马柳,女"};
    testConsumer03(names, (str) -> {
                System.out.print(str.split(",")[0]+":");
            }, (str) -> {
                System.out.println(str.split(",")[1]);
            }
    );
}
public void testConsumer03(String[] names, Consumer<String> consumer1, Consumer<String> consumer2) {
    for (int i = 0; i < names.length; i++) {
        //第一个消费接口处理完,第二个消费接口接着处理
        consumer1.andThen(consumer2).accept(names[i]);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# 供给型接口Supplier

提供一个抽象方法get(),返回一个T

package java.util.function;
/**
 * Represents a supplier of results.
 *
 * <p>There is no requirement that a new or distinct result be returned each
 * time the supplier is invoked.
 *
 * <p>This is a <a href="package-summary.html">functional interface</a>
 * whose functional method is {@link #get()}.
 *
 * @param <T> the type of results supplied by this supplier
 *
 * @since 1.8
 */
@FunctionalInterface
public interface Supplier<T> {
    /**
     * Gets a result.
     *
     * @return a result
     */
    T get();
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

# 供给型接口Supplier的例子

利用供给型接口获取10个100内随机数,输出

    @Test
    public void test04() {
        ArrayList<Integer> arr = testSupplier02(10, () -> (int) (Math.random()*100));
        for (int i = 0; i < arr.size(); i++) {
            System.out.println(i + arr.get(i));
        }
    }

    public ArrayList<Integer> testSupplier02(int size, Supplier<Integer> consumer) {
        ArrayList<Integer> arr = new ArrayList<>();
        for (int i = 0; i < size; i++) {
            int str = consumer.get();
            arr.add(str);
        }
        return arr;
    }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

# 函数型接口Function

提供一个抽象方法apply(T t),返回一个R,Function<T,V> compose(Function function)在apply前执行,Function<T,V> andThen(Function function)在apply之后执行。

package java.util.function;
import java.util.Objects;
/**
 * Represents a function that accepts one argument and produces a result.
 *
 * <p>This is a <a href="package-summary.html">functional interface</a>
 * whose functional method is {@link #apply(Object)}.
 *
 * @param <T> the type of the input to the function
 * @param <R> the type of the result of the function
 *
 * @since 1.8
 */
@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);
    /**
     * Returns a composed function that first applies the {@code before}
     * function to its input, and then applies this function to the result.
     * If evaluation of either function throws an exception, it is relayed to
     * the caller of the composed function.
     *
     * @param <V> the type of input to the {@code before} function, and to the
     *           composed function
     * @param before the function to apply before this function is applied
     * @return a composed function that first applies the {@code before}
     * function and then applies this function
     * @throws NullPointerException if before is null
     *
     * @see #andThen(Function)
     */
    default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
        Objects.requireNonNull(before);
        return (V v) -> apply(before.apply(v));
    }
    /**
     * Returns a composed function that first applies this function to
     * its input, and then applies the {@code after} function to the result.
     * If evaluation of either function throws an exception, it is relayed to
     * the caller of the composed function.
     *
     * @param <V> the type of output of the {@code after} function, and of the
     *           composed function
     * @param after the function to apply after this function is applied
     * @return a composed function that first applies this function and then
     * applies the {@code after} function
     * @throws NullPointerException if after is null
     *
     * @see #compose(Function)
     */
    default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
        Objects.requireNonNull(after);
        return (T t) -> after.apply(apply(t));
    }
    /**
     * Returns a function that always returns its input argument.
     *
     * @param <T> the type of the input and output objects to the function
     * @return a function that always returns its input argument
     */
    static <T> Function<T, T> identity() {
        return t -> t;
    }
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71

# 函数型接口Function例子

把一个字符串,先转大写然后反转,最后转小写打印,采用三个函数型接口参数

@Test
public void test3() {
    String msg = textFunction("hello-LAmbda", (str1) -> {
        return  str1.toUpperCase();
    },(str1)->{
        return str1.toLowerCase();
    },(str1)->{
        return new StringBuilder(str1).reverse().toString();
    });
    System.out.println(msg);
}

public String textFunction(String str, Function<String, String> function, Function<String, String> function2, Function<String, String> function3) {
    // function先执行然后是function2,但是function3在function2之前执行所以function2最后执行~~~
    return function.andThen(function2.compose(function3)).apply(str);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# 断言型接口 Predicate

一个抽象方法test(Test t),默认方法 and(Predicate predicate)/or(Predicate predicate)/negate(Predicate predicate)静态方法isEqual(Object targetRef)

package java.util.function;
import java.util.Objects;
/**
 * Represents a predicate (boolean-valued function) of one argument.
 *
 * <p>This is a <a href="package-summary.html">functional interface</a>
 * whose functional method is {@link #test(Object)}.
 *
 * @param <T> the type of the input to the predicate
 *
 * @since 1.8
 */
@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);

    /**
     * Returns a composed predicate that represents a short-circuiting logical
     * AND of this predicate and another.  When evaluating the composed
     * predicate, if this predicate is {@code false}, then the {@code other}
     * predicate is not evaluated.
     *
     * <p>Any exceptions thrown during evaluation of either predicate are relayed
     * to the caller; if evaluation of this predicate throws an exception, the
     * {@code other} predicate will not be evaluated.
     *
     * @param other a predicate that will be logically-ANDed with this
     *              predicate
     * @return a composed predicate that represents the short-circuiting logical
     * AND of this predicate and the {@code other} predicate
     * @throws NullPointerException if other is null
     */
    default Predicate<T> and(Predicate<? super T> other) {
        Objects.requireNonNull(other);
        return (t) -> test(t) && other.test(t);
    }

    /**
     * Returns a predicate that represents the logical negation of this
     * predicate.
     *
     * @return a predicate that represents the logical negation of this
     * predicate
     */
    default Predicate<T> negate() {
        return (t) -> !test(t);
    }

    /**
     * Returns a composed predicate that represents a short-circuiting logical
     * OR of this predicate and another.  When evaluating the composed
     * predicate, if this predicate is {@code true}, then the {@code other}
     * predicate is not evaluated.
     *
     * <p>Any exceptions thrown during evaluation of either predicate are relayed
     * to the caller; if evaluation of this predicate throws an exception, the
     * {@code other} predicate will not be evaluated.
     *
     * @param other a predicate that will be logically-ORed with this
     *              predicate
     * @return a composed predicate that represents the short-circuiting logical
     * OR of this predicate and the {@code other} predicate
     * @throws NullPointerException if other is null
     */
    default Predicate<T> or(Predicate<? super T> other) {
        Objects.requireNonNull(other);
        return (t) -> test(t) || other.test(t);
    }

    /**
     * Returns a predicate that tests if two arguments are equal according
     * to {@link Objects#equals(Object, Object)}.
     *
     * @param <T> the type of arguments to the predicate
     * @param targetRef the object reference with which to compare for equality,
     *               which may be {@code null}
     * @return a predicate that tests if two arguments are equal according
     * to {@link Objects#equals(Object, Object)}
     */
    static <T> Predicate<T> isEqual(Object targetRef) {
        return (null == targetRef)
                ? Objects::isNull
                : object -> targetRef.equals(object);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93

# 断言型接口Predicate例子

判断一个字符串是否包含‘*’并且长度大于10 或者 字符串包含‘-’

    public void test4() {
        String str = "hello-lambda";
        boolean bool = textPredicate(str, (msg) -> {
            return msg.indexOf('*') > -1;
        }, (msg) -> {
            return msg.length() > 10;
        }, (msg) -> {
            return msg.indexOf("-") > -1;
        });
        System.out.println(bool);
    }

    public boolean textPredicate(String str, Predicate<String> predicate, Predicate<String> predicate2, Predicate<String> predicate3) {
        return predicate.and(predicate2).or(predicate3).test(str);
    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# 其他接口

根据上面的示例,每个接口结合源码可以写出自己想要的代码

函数式接口 参数类型 返回类型 用途
BiFunction<T, U, R> T, U R 对类型为 T, U 参数应用 操作,返回 R 类型的结 果。包含方法为 R apply(T t, U u);
UnaryOperator (Function子接口) T T 对类型为T的对象进行一 元运算,并返回T类型的 结果。包含方法为 T apply(T t);
BinaryOperator (BiFunction 子接口) T, T T 对类型为T的对象进行二 元运算,并返回T类型的 结果。包含方法为 T apply(T t1, T t2);
BiConsumer<T, U> T, U void 对类型为T, U 参数应用 操作。包含方法为 void accept(T t, U u)
ToIntFunction ToLongFunction ToDoubleFunction T int long double 分别计算int、long、 double、值的函数
IntFunction LongFunction DoubleFunction int long double R 参数分别为int、long、 double 类型的函数

# 我的小结

函数式接口是构建lambda表达式的一个前提,只有弄清楚了,接口的定义参数和返回类型,后面的lambda表达式按照语法格式就好写了,接口作为参数可以传多个,根据相应的方法,组合搭配出自己想要的功能。使用每一个接口首先看以下源码每个方法的作用然后熟练应用可以学的更快。

文章首发于 黑猫のBlog (opens new window) 欢迎来留言啊!!!

Last Updated: 8/20/2021, 5:31:07 PM