java8新特性之函數式接口

函數式接口

上篇 java8新特性之Lambda表達式,說了如下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();
}
咱們也能夠本身定義函數式接口,定義一個接口,使用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);
}
其實咱們不用本身定義這些函數式接口,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);
         };
    }
}

消費型接口Consumer的三個例子

1.Consumer接口的accept消費一個參數(反轉字符串)html

@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);
}

2.兩個消費接口 調用accept,實現一個輸出字符串大寫,一個輸出字符串輸出小寫java

@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);
}

3.兩個消費接口的accept和andThen組合操做字符app

@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]);
    }
}

供給型接口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();
}

供給型接口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;
    }

函數型接口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;
    }
}

函數型接口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);
}

斷言型接口 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);
    }
}

斷言型接口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);
    }

其餘接口

根據上面的示例,每一個接口結合源碼能夠寫出本身想要的代碼
函數式接口 參數類型 返回類型 用途
BiFunction<T, U, R> T, U R 對類型爲 T, U 參數應用 操做,返回 R 類型的結 果。包含方法爲 R apply(T t, U u);
UnaryOperator<T> (Function子接口) T T 對類型爲T的對象進行一 元運算,並返回T類型的 結果。包含方法爲 T apply(T t);
BinaryOperator<T> (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<T> ToLongFunction<T> ToDoubleFunction<T> T int long double 分別計算int、long、 double、值的函數
IntFunction<R> LongFunction<R> DoubleFunction<R> int long double R 參數分別爲int、long、 double 類型的函數

個人小結

函數式接口是構建lambda表達式的一個前提,只有弄清楚了,接口的定義參數和返回類型,後面的lambda表達式按照語法格式就好寫了,接口做爲參數能夠傳多個,根據相應的方法,組合搭配出本身想要的功能。使用每個接口首先看如下源碼每一個方法的做用而後熟練應用能夠學的更快。

文章首發於 黑貓のBlog 歡迎來留言啊!!!less

相關文章
相關標籤/搜索