java8新特性——Lambda表達(dá)式和函數(shù)式接口

Java Lambda概要

  • Java Lambda表達(dá)式是一種匿名函數(shù)屎篱;它沒(méi)有聲明的方法迅矛,即沒(méi)有訪問(wèn)修飾符简珠、返回值聲明和名字外盯。

Java Lambda表達(dá)式基本語(yǔ)法

  • Java中的Lambda表達(dá)式基本語(yǔ)法:
    • (argument) -> {body}
  • 比如說(shuō):
    • (arg1,arg2) -> {body}
    • (type1 arg1,type2 arg2) -> {body}
    • arg1 -> {body}
    • s -> System.out.println(s)

Java Lambda表達(dá)式結(jié)構(gòu)

  • 一個(gè)Lambda表達(dá)式可以有0個(gè)或多個(gè)參數(shù)。
  • 參數(shù)的類型既可以明確聲明挺尿,也可以根據(jù)上下文來(lái)推斷奏黑。例如,(int a)和(a)效果相同
  • 所有參數(shù)需包含在圓括號(hào)內(nèi)编矾,參數(shù)之間用逗號(hào)相隔熟史。例如:(a,b)或(int a,int b)或(String a,String b,float c)
  • 空?qǐng)A括號(hào)代表參數(shù)集為空。例如:() -> 42
  • 當(dāng)只有一個(gè)參數(shù)窄俏,且其類型可以推導(dǎo)時(shí)蹂匹,圓括號(hào)()可省略。例如:a -> return a * a
  • Lambda表達(dá)式的主題可以包含0條或多條語(yǔ)句
  • 如果Lambda表達(dá)式的主題只有一條語(yǔ)句凹蜈,花括號(hào){}可以省略限寞。匿名函數(shù)的返回類型與該主體表達(dá)式一致
  • 如果Lambda表達(dá)式的主體包含一條以上語(yǔ)句,則表達(dá)式主體必須包含在花括號(hào){}中(形成代碼塊)仰坦,匿名函數(shù)的返回類型與代碼塊的返回類型一致履植,若沒(méi)有返回則為空

Java Lambda示例

  • Lambda示例說(shuō)明
    • (int a,int b) -> {return a + b;}
    • () -> System.out.println("hello world");
    • (String s) -> {System.out.println(s);}
    • () -> 42
    • () -> {return 3.1415};

Lambda表達(dá)式作用

  • 傳遞行為,而不僅僅是值
  • 提升抽象層次
  • API重用性更好
  • 更加靈活

方法引用

其實(shí)是lambda表達(dá)式的一種簡(jiǎn)化寫法悄晃。所引用的方法其實(shí)是lambda表達(dá)式的方法體實(shí)現(xiàn)玫霎,語(yǔ)法也很簡(jiǎn)單,左邊是容器(可以是類名妈橄,實(shí)例名)鼠渺,中間是"::",右邊是相應(yīng)的方法名眷细。
本質(zhì)上可以將方法引用看作是一個(gè)“函數(shù)指針” ——function pointer。

如下所示:

ObjectReference::methodName
一般方法的引用格式:

  • 1鹃祖、如果是靜態(tài)方法溪椎,則是ClassName::staticmethodName。如 Object ::equals
  • 2、如果是實(shí)例方法校读,則是Instance::methodName沼侣。如Object obj=new Object();obj::equals;
  • 3、如果是實(shí)例方法歉秫,則可以ClassName::methodName蛾洛。如Object ::equals;
  • 4、構(gòu)造函數(shù).則是ClassName::new

方法引用使用場(chǎng)景:lambda表達(dá)式只有一行代碼雁芙,且這行代碼調(diào)用的這個(gè)方法是已經(jīng)存在的轧膘,那么就可以用方法引用去替換掉lambda表達(dá)式。

此為方法引用最簡(jiǎn)單例子:

public class MethodReferenceTest {

    public static void main(String[] args) {
        List<String> list = Arrays.asList("hello","world","helloworld");
        list.forEach(item -> System.out.println());

        list.forEach(System.out::println);
    }
}

接下來(lái)看一下稍微復(fù)雜的例子:

1兔甘、如果是靜態(tài)方法谎碍,則是ClassName::staticmethodName。如 Object ::equals

public class Student {

    private String name;

    private int score;

    public Student() {
    }

    public Student(String name, int score) {
        this.name = name;
        this.score = score;
    }

    public String getName() {
        return name;
    }

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

    public int getScore() {
        return score;
    }

    public void setScore(int score) {
        this.score = score;
    }

    public static int compareStudentBySocre(Student student1,Student student2){
        return  student1.getScore() - student2.getScore();
    }

    public static int compareStudentByName(Student student1,Student student2){
        return  student1.getName().compareTo(student2.getName());
    }   
}

public class MethodReferenceTest2 {

    public static void main(String[] args) {
        Student student1 = new Student("zhangsan",80);
        Student student2 = new Student("lisi",90);
        Student student3 = new Student("wangwu",70);
        Student student4 = new Student("zhaoliu",60);

        List<Student> list = Arrays.asList(student1,student2,student3,student4);

        list.sort((studentParam1,studentParam2) -> Student.compareStudentBySocre(studentParam1,studentParam2));
        list.sort((studentParam1,studentParam2) -> Student.compareStudentByName(studentParam1,studentParam2));
        System.out.println(list);

        list.sort(Student::compareStudentBySocre);
        list.sort(Student::compareStudentByName);
        System.out.println(list);
    }
}

2洞焙、如果是實(shí)例方法蟆淀,則是Instance::methodName。如Object obj=new Object();obj::equals;

public class StudentComparator {

    public int compareStudentBySocre(Student student1,Student student2){
        return  student1.getScore() - student2.getScore();
    }

    public int compareStudentByName(Student student1,Student student2){
        return  student1.getName().compareTo(student2.getName());
    }
}

public class MethodReferenceTest2 {

    public static void main(String[] args) {
        Student student1 = new Student("zhangsan",80);
        Student student2 = new Student("lisi",90);
        Student student3 = new Student("wangwu",70);
        Student student4 = new Student("zhaoliu",60);

        List<Student> list = Arrays.asList(student1,student2,student3,student4);
      
        StudentComparator studentComparator = new StudentComparator();
        list.sort((studentParam1,studentParam2) -> studentComparator.compareStudentBySocre(studentParam1,studentParam2));
        list.sort((studentParam1,studentParam2) -> studentComparator.compareStudentByName(studentParam1,studentParam2));
        list.sort(studentComparator::compareStudentBySocre);
        list.sort(studentComparator::compareStudentByName);
    }   
}

3澡匪、如果是實(shí)例方法熔任,則可以ClassName::methodName。如Object ::equals;

public class Student {

   ......字段唁情、setter和getter方法省略......

    public int compareBySocre(Student student){
        return  this.getScore() - student.getScore();
    }

    public int compareByName(Student student){
        return  this.getName().compareTo(student.getName());
    }
}

public class MethodReferenceTest2 {

    public static void main(String[] args) {
        Student student1 = new Student("zhangsan",80);
        Student student2 = new Student("lisi",90);
        Student student3 = new Student("wangwu",70);
        Student student4 = new Student("zhaoliu",60);

        List<Student> list = Arrays.asList(student1,student2,student3,student4);

        list.sort(Student::compareBySocre);
        list.sort(Student::compareByName);
    }
}

4疑苔、構(gòu)造函數(shù).則是ClassName::new

public class MethodReferenceTest2 {

    public static void main(String[] args) {
        List<String> citys = Arrays.asList("shenzhen","shanghai","beijing","chongqing");
        Collections.sort(citys,String::compareTo);

        MethodReferenceTest2 methodReferenceTest2 = new MethodReferenceTest2();
        String test2String = methodReferenceTest2.getString(Student::new);
        String string2 = methodReferenceTest2.getString2("  hello  ", String::new);
    }

    public <T> String getString(Supplier<T> supplier){
        return supplier.get() + "test";
    }

    public <T> T getString2(T str, Function<T,T> function){
        return function.apply(str);
    }
}

可以看出,doSomething方法就是lambda表達(dá)式的實(shí)現(xiàn)荠瘪,這樣的好處就是夯巷,如果你覺(jué)得lambda的方法體會(huì)很長(zhǎng),影響代碼可讀性哀墓,方法引用就是個(gè)解決辦法趁餐。

函數(shù)式接口

函數(shù)式接口(functional interface 也叫功能性接口,其實(shí)是同一個(gè)東西)篮绰。簡(jiǎn)單來(lái)說(shuō)后雷,函數(shù)式接口是只包含一個(gè)方法的接口。比如Java標(biāo)準(zhǔn)庫(kù)中的java.lang.Runnable和 java.util.Comparator都是典型的函數(shù)式接口吠各。
java 8提供 @FunctionalInterface作為注解,這個(gè)注解是非必須的臀突,只要接口符合函數(shù)式接口的標(biāo)準(zhǔn)(即只包含一個(gè)方法的接口),虛擬機(jī)會(huì)自動(dòng)判斷贾漏, 但 最好在接口上使用注解@FunctionalInterface進(jìn)行聲明候学,以免團(tuán)隊(duì)的其他人員錯(cuò)誤地往接口中添加新的方法。

Java中的lambda無(wú)法單獨(dú)出現(xiàn)纵散,它需要一個(gè)函數(shù)式接口來(lái)盛放梳码,lambda表達(dá)式方法體其實(shí)就是函數(shù)接口的實(shí)現(xiàn)隐圾;每個(gè)lambda表達(dá)式都能隱式的賦值給函數(shù)式接口。

關(guān)于函數(shù)式接口:

  • 1掰茶、如果一個(gè)接口只有一個(gè)抽象方法暇藏,那么該接口就是一個(gè)函數(shù)式接口。
  • 2濒蒋、如果我們?cè)谀硞€(gè)接口上聲明了@FunctionalInterface注解盐碱,那么編譯器就會(huì)按照函數(shù)式接口的定義來(lái)要求該接口。
  • 3沪伙、如果某個(gè)接口只有一個(gè)抽象方法瓮顽,但我們并沒(méi)有給該接口聲明@FunctionalInterface注解,那么編譯器依舊會(huì)將該接口看做是函數(shù)式接口焰坪。

高階函數(shù)

如果一個(gè)函數(shù)接收另一個(gè)函數(shù)作為參數(shù)趣倾,或者返回一個(gè)函數(shù)作為返回值,那么該函數(shù)就叫做高階函數(shù)某饰。

自定義函數(shù)式接口

public class Test3 {

    public static void main(String[] args) {
        TheInterface i1 = () -> {};
        System.out.println(i1.getClass().getInterfaces()[0]);

        TheInterface2 i2 = () -> {};
        System.out.println(i2.getClass().getInterfaces()[0]);     
    }
}

@FunctionalInterface
interface TheInterface{
    void myMethod();
}

@FunctionalInterface
interface TheInterface2{
    void myMethod2();
}

我們知道使用Lambda表達(dá)式是需要使用函數(shù)式接口的儒恋,那么,豈不是在我們開發(fā)過(guò)程中需要定義許多函數(shù)式接口黔漂,其實(shí)不然诫尽,java8其實(shí)已經(jīng)為我們定義好了4類內(nèi)置函數(shù)式接口,這4類接口其實(shí)已經(jīng)可以解決我們開發(fā)過(guò)程中絕大部分的問(wèn)題炬守,只有一小部分比較特殊得情況需要我們自己去定義函數(shù)式接口牧嫉,下面就簡(jiǎn)單來(lái)學(xué)習(xí)一下java8內(nèi)置得4大核心函數(shù)式接口。

一减途、Consumer<T>:消費(fèi)型接口(void accept(T t))
Consumer接口:該接口定義了一個(gè)void accept(T)的抽象方法酣藻,其函數(shù)描述符為 (T) -> void,如果你需要一個(gè)操作某一對(duì)象鳍置,但無(wú)需返回的的函數(shù)式接口辽剧,那么就可以使用Consumer接口。

@FunctionalInterface
public interface Consumer<T> {

    void accept(T t);

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

來(lái)看一個(gè)簡(jiǎn)單得例子:

public class Test3 {

    public static void main(String[] args) {
       
        List<String> list = Arrays.asList("hello","world","helloworld");
        list.forEach(item -> System.out.println(item.toUpperCase()));

        List<String> list2 = new ArrayList<>();
        list.forEach(item -> list2.add(item.toUpperCase()));
        list2.forEach(item -> System.out.println(item));

        list.stream().map(item -> item.toUpperCase()).forEach(item -> System.out.println(item));
        list.stream().map(String::toUpperCase).forEach(System.out::println);
    }
}

以上為消費(fèi)型接口税产,有參數(shù)怕轿,無(wú)返回值類型的接口。

二辟拷、Supplier<T>:供給型接口(T get())
Supplier接口:既然有消費(fèi)者接口(Consumer)撞羽,那就要有生產(chǎn)者接口(Supplier),該接口定義了一個(gè) T get() 的抽象方法衫冻,其函數(shù)描述符為 () -> T诀紊,如果你需要一個(gè)從某一對(duì)象獲取到某值的接口,那么就可以用Supplier隅俘。

@FunctionalInterface
public interface Supplier<T> {

    /**
     * Gets a result.
     *
     * @return a result
     */
    T get();
}

來(lái)看一個(gè)簡(jiǎn)單得例子:

public class SupplierTest {

    public static void main(String[] args) {
        Supplier<String> supplier = () -> "hello world";
        System.out.println(supplier.get());

        Supplier<Student> supplier1 = () -> new Student("張三",20);
        System.out.println(supplier1.get().getName());

        Supplier<Student> supplier2 = Student::new;
        System.out.println(supplier2.get().getName());
    }
}

public class Student {

    private String name;

    private int age;

    public Student() {
    }

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

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

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

上面就是一個(gè)供給類型得接口邻奠,只有產(chǎn)出到推,沒(méi)人輸入,就是只有返回值惕澎,沒(méi)有入?yún)?/p>

三、Function<T, R>:函數(shù)型接口(R apply(T t))
Function接口:該接口定義了一個(gè) R applay(T)類型的抽象函數(shù)颜骤,它接受一個(gè)泛型變量T唧喉,并返回一個(gè)泛型變量R,如果你需要將一個(gè)對(duì)象T映射成R忍抽,那么就可以使用Function接口八孝。例如我們需要打印一個(gè)花名冊(cè),但是具體打印的格式需要可靈活配置鸠项,那就將花名冊(cè)打印格式參數(shù)化干跛,需要使用 (Student) -> String 類型的函數(shù)描述符,與Function接口吻合祟绊。

@FunctionalInterface
public interface Function<T, R> {

    R apply(T t);

    default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
        Objects.requireNonNull(before);
        return (V v) -> apply(before.apply(v));
    }

    default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
        Objects.requireNonNull(after);
        return (T t) -> after.apply(apply(t));
    }

    static <T> Function<T, T> identity() {
        return t -> t;
    }
}

下面看一個(gè)簡(jiǎn)單的例子:

public class FunctionTest {

    public static void main(String[] args) {
        FunctionTest test = new FunctionTest();
        int compute1 = test.compute(1, value -> {return 2 * value;});
        System.out.println(compute1);

        int compute2 = test.compute(2, value -> 5 + value);
        System.out.println(compute2);

        int compute3 = test.compute(3, value -> value * value);
        System.out.println(compute3);

        String compute4 = test.convert(5,value -> String.valueOf(value + "helloworld"));
        System.out.println(compute4);

        Function<Integer,Integer> function = value -> value * 2;
        int compute5 = test.compute(4, function);
        System.out.println(compute5);

    }

    public <T> T compute(T a, Function<T,T> function){
        T result = function.apply(a);
        return result;
    }

    public String convert(int a,Function<Integer,String> function){
        String result = function.apply(a);
        return result;
    }
}

下面看一個(gè)稍微復(fù)雜的例子:

public class FunctionTest2 {

    public static void main(String[] args) {
        FunctionTest2 test = new FunctionTest2();

        int compute = test.compute(2, value -> value * 3, value -> value * value);
        int compute2 = test.compute2(2, value -> value * 3, value -> value * value);
        System.out.println(compute);
        System.out.println(compute2);
    }

    public <T> T compute(T a, Function<T,T> function1,Function<T,T> function2){
        return function1.compose(function2).apply(a);
    }

    public <T> T compute2(T a,Function<T,T> function1,Function<T,T> function2){
        return function1.andThen(function2).apply(a);
    }
}

上面就是一個(gè)函數(shù)型接口楼入,輸入一個(gè)類型得參數(shù),輸出一個(gè)類型得參數(shù)牧抽,當(dāng)然兩種類型可以一致嘉熊。

四、Predicate<T>:斷言型接口(boolean test(T t))
Predicate接口:該接口定義了一個(gè)支持泛型的boolean test( T)的抽象方法扬舒,其函數(shù)描述符為 (T)-> boolean阐肤,現(xiàn)在我們就可以直接使用Predicate接口來(lái)替代OToBoolean接口了。

@FunctionalInterface
public interface Predicate<T> {

    boolean test(T t);

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

    default Predicate<T> negate() {
        return (t) -> !test(t);
    }

    default Predicate<T> or(Predicate<? super T> other) {
        Objects.requireNonNull(other);
        return (t) -> test(t) || other.test(t);
    }

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

下面看一個(gè)簡(jiǎn)單得例子:

public class PredicateTest {

    public static void main(String[] args) {
        Predicate<String> predicate = p -> p.length() > 5;
        boolean test = predicate.test("hello");
        System.out.println(test);
    }
}

下面看一個(gè)稍微復(fù)雜的例子:

public class PredicateTest2 {

    public static void main(String[] args) {
        List<Integer> list = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
        conditionFilter(list,item -> item % 2 == 0);

        conditionFilter(list,item -> item > 5);

        conditionFilter(list,item -> true);

        //找出集合中所有大于5且為偶數(shù)的元素
        conditionFilter2(list,item -> item > 5,item -> item % 2 == 0);

        //找出集合中大于5或?yàn)榕紨?shù)的元素
        conditionFilter3(list,item -> item > 5,item -> item % 2 == 0);

        //找出集合中所有大于5且為偶數(shù)的元素基礎(chǔ)上取反
        conditionFilter4(list,item -> item > 5,item -> item % 2 == 0);

        boolean test1 = isEqual("hello").test("hello");
        System.out.println(test1);

        boolean test2 = isEqual(new Date()).test(new Date());
        System.out.println(test2);

    }

    public static <T> void conditionFilter(List<T> list, Predicate<T> predicate){
        list.forEach(t -> {
            if(predicate.test(t)){
                System.out.println(t);
            }
        });
    }

    public static <T> void conditionFilter2(List<T> list,Predicate<T> predicate1,Predicate<T> predicate2){
        list.forEach(item -> {
            if(predicate1.and(predicate2).test(item)){
                System.out.println(item);
            }
        });
    }

    public static <T> void conditionFilter3(List<T> list,Predicate<T> predicate1,Predicate<T> predicate2){
        list.forEach(item -> {
            if(predicate1.or(predicate2).test(item)){
                System.out.println(item);
            }
        });
    }

    public static <T> void conditionFilter4(List<T> list,Predicate<T> predicate1,Predicate<T> predicate2){
        list.forEach(item -> {
            if(predicate1.and(predicate2).negate().test(item)){
                System.out.println(item);
            }
        });
    }

    public static <T> Predicate<T> isEqual(T t){
        return Predicate.isEqual(t);
    }

}

上面就是一個(gè)斷言型接口讲坎,輸入一個(gè)參數(shù)孕惜,輸出一個(gè)boolean類型得返回值。

五晨炕、其他的一些重要函數(shù)式接口
**BiFunction<T, U, R> **

參數(shù)類型有2個(gè)衫画,為T,U,返回值為R,其中方法為R apply(T t, U u)

@FunctionalInterface
public interface BiFunction<T, U, R> {

    R apply(T t, U u);

    default <V> BiFunction<T, U, V> andThen(Function<? super R, ? extends V> after) {
        Objects.requireNonNull(after);
        return (T t, U u) -> after.apply(apply(t, u));
    }
}

下面看一個(gè)簡(jiǎn)單得例子:

public class FunctionTest2 {

    public static void main(String[] args) {
        FunctionTest2 test = new FunctionTest2();

        int compute3 = test.compute3(2, 3, (value1, value2) -> value1 + value2);
        System.out.println(compute3);

        int compute4 = test.compute4(3, 4, (value1, value2) -> value1 + value2, value -> value * value);
        System.out.println(compute4);
    }

    public <T> T compute3(T a, T b, BiFunction<T,T,T> function){
        return function.apply(a,b);
    }

    public <T> T compute4(T a,T b,BiFunction<T,T,T> biFunction,
                        Function<T,T> function){
        return biFunction.andThen(function).apply(a,b);
    }
}

public class PersonTest {

    public static void main(String[] args) {
        Person person1 = new Person("zhangsan",20);
        Person person2 = new Person("lisi",38);
        Person person3 = new Person("wangwu",40);
        List<Person> list = Arrays.asList(person1,person2,person3);

        List<Person> resultList = getPersonByUsername("zhangsan", list);
        resultList.forEach(person -> System.out.println(person));

        List<Person> personByAge = getPersonByAge(20, list);
        personByAge.forEach(person -> System.out.println(person));

        List<Person> personByAge2 = getPersonByAge2(20, list, (age, personList) ->
                personList.stream().filter(person -> person.getAge() > age).collect(Collectors.toList())
        );
        personByAge2.forEach(person -> System.out.println(person));
    }

    public static List<Person> getPersonByUsername(String username,List<Person> list){
        return list.stream().filter(person -> person.getName().equals(username)).collect(Collectors.toList());
    }

    public static List<Person> getPersonByAge(int age,List<Person> list){
        /*BiFunction<Integer,List<Person>,List<Person>> biFunction = (ageOfPerson,personList) -> {
            return personList.stream().filter(person -> person.getAge() > ageOfPerson).collect(Collectors.toList());
        };*/
        //這里下面一行代碼和上面注釋掉的代碼一個(gè)意思疹味,只是寫法更簡(jiǎn)潔
        BiFunction<Integer,List<Person>,List<Person>> biFunction = (ageOfPerson,personList) ->
            personList.stream().filter(person -> person.getAge() > ageOfPerson).collect(Collectors.toList());

        return biFunction.apply(age,list);
    }

    public static <T> List<T> getPersonByAge2(int age,List<T> list,BiFunction<Integer,List<T>,List<T>> biFunction){
        return biFunction.apply(age,list);
    }
}

BinaryOperator<T>(BiFunction子接口)

參數(shù)為T邦邦,對(duì)參數(shù)為T得對(duì)象進(jìn)行二元操作,并返回T類型得結(jié)果榨汤,其中方法為T apply(T t1, T t2)

@FunctionalInterface
public interface BinaryOperator<T> extends BiFunction<T,T,T> {
    
    public static <T> BinaryOperator<T> minBy(Comparator<? super T> comparator) {
        Objects.requireNonNull(comparator);
        return (a, b) -> comparator.compare(a, b) <= 0 ? a : b;
    }

    public static <T> BinaryOperator<T> maxBy(Comparator<? super T> comparator) {
        Objects.requireNonNull(comparator);
        return (a, b) -> comparator.compare(a, b) >= 0 ? a : b;
    }
}

下面看一個(gè)簡(jiǎn)單得例子:

public class BinaryOperatorTest {

    public static void main(String[] args) {
        Integer compute = compute(3, 5, (value1, value2) -> value1 + value2);
        System.out.println(compute);

        String min1 = getShortMin("hello", "world", (value1, value2) -> value1.compareTo(value2));
        System.out.println(min1);
        String min2 = getShortMin("hello", "world", (value1, value2) -> value1.charAt(0) - value2.charAt(0));
        System.out.println(min2);

        String max = getShortMax("hello", "world", (value1, value2) -> value1.charAt(0) - value2.charAt(0));
        System.out.println(max);

    }

    public static <T> T compute(T a, T b, BinaryOperator<T> binaryOperator){
        return binaryOperator.apply(a,b);
    }

    /**
     * 返回兩個(gè)數(shù)的較小值
     * @param a
     * @param b
     * @param comparator
     * @param <T>
     * @return
     */
    public static <T> T getShortMin(T a, T b, Comparator<T> comparator){
        return BinaryOperator.minBy(comparator).apply(a,b);
    }

    /**
     * 返回兩個(gè)數(shù)的較大值
     * @param a
     * @param b
     * @param comparator
     * @param <T>
     * @return
     */
    public static <T> T getShortMax(T a, T b, Comparator<T> comparator){
        return BinaryOperator.maxBy(comparator).apply(a,b);
    }
}

Comparator<T>接口

@FunctionalInterface
public interface Comparator<T> {
   
    int compare(T o1, T o2);
}

下面看一個(gè)簡(jiǎn)單得例子:

public class StringComparator {

    public static void main(String[] args) {
        List<String> list = Arrays.asList("zhangsan","wangwu","lisi","zhaoliu");
        Collections.sort(list, new Comparator<String>() {
            @Override
            public int compare(String o1, String o2) {
                return o2.compareTo(o1);
            }
        });
        System.out.println(list);

        Collections.sort(list,(o1,o2) -> {
            return o2.compareTo(o1);
        });
        System.out.println(list);

        //expression o2.compareTo(o1)
        //statement {return o2.compareTo(o1);}

        Collections.sort(list,(o1,o2) -> o2.compareTo(o1));
        System.out.println(list);

        Collections.sort(list,Comparator.reverseOrder());
        System.out.println(list);
    }
}

Runnable接口

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

下面看一個(gè)簡(jiǎn)單得例子:

new Thread(() -> System.out.println("hello world")).start();

除了上述得類型得接口外還有其他的一些接口供我們使用:

1).BiFunction<T, U, R>

參數(shù)類型有2個(gè),為T秆撮,U,返回值為R换况,其中方法為R apply(T t, U u)

2).UnaryOperator<T>(Function子接口)

參數(shù)為T职辨,對(duì)參數(shù)為T的對(duì)象進(jìn)行一元操作盗蟆,并返回T類型結(jié)果,其中方法為T apply(T t)

3).BinaryOperator<T>(BiFunction子接口)

參數(shù)為T舒裤,對(duì)參數(shù)為T得對(duì)象進(jìn)行二元操作喳资,并返回T類型得結(jié)果,其中方法為T apply(T t1腾供, T t2)

4).BiConsumcr(T, U)

參數(shù)為T仆邓,U無(wú)返回值,其中方法為 void accept(T t, U u)

5).ToIntFunction<T>伴鳖、ToLongFunction<T>节值、ToDoubleFunction<T>

參數(shù)類型為T,返回值分別為int榜聂,long搞疗,double,分別計(jì)算int须肆,long匿乃,double得函數(shù)。

6).IntFunction<R>休吠、LongFunction<R>扳埂、DoubleFunction<R>

參數(shù)分別為int,long瘤礁,double阳懂,返回值為R。

以上就是java8內(nèi)置得核心函數(shù)式接口柜思,其中包括了大部分得方法類型岩调,所以可以在使用得時(shí)候根據(jù)不同得使用場(chǎng)景去選擇不同得接口使用。

這里的函數(shù)式接口可以理解成赡盘,定義了很多不同的參數(shù)個(gè)數(shù)待實(shí)現(xiàn)接口号枕。

Java8內(nèi)置了很多函數(shù)式接口(在java.util.function包下的接口),下面表格有列出:

接口 方法 參數(shù) 返回值
Function R apply(T t) 一個(gè)參數(shù) R
DoubleFunction R apply(double value) 一個(gè)參數(shù) R
IntFunction R apply(int value) 一個(gè)參數(shù) R
UnaryOperator extends Function T apply(T t) 一個(gè)參數(shù) T

接口 方法 參數(shù) 返回值
BiFunction R apply(T t, U u) 兩個(gè)參數(shù) R
BinaryOperator extends BiFunction T apply(T t, T u) 兩個(gè)參數(shù) T
DoubleBinaryOperator double applyAsDouble(double l, double r) 兩個(gè)參數(shù) double
DoubleBinaryOperator double applyAsDouble(double l, double r) 兩個(gè)參數(shù)
double
DoubleUnaryOperator double applyAsDouble(double operand) 一個(gè)參數(shù) double
IntBinaryOperator int applyAsInt(int left, int right) 兩個(gè)參數(shù) int
LongBinaryOperator long applyAsLong(long left, long right) 兩個(gè)參數(shù) long

接口 方法 參數(shù) 返回值
DoubleToIntFunction int applyAsInt(double value) 一個(gè)參數(shù) int
IntToLongFunction long applyAsLong(int value) 一個(gè)參數(shù) long
DoubleToLongFunction long applyAsLong(double value) 一個(gè)參數(shù) long
IntToDoubleFunction double applyAsDouble(int value) 一個(gè)參數(shù) double

接口 方法 參數(shù) 返回值
Predicate boolean test(T t) 一個(gè)參數(shù) boolean
BiPredicate boolean test(T t, U u) 兩個(gè)參數(shù) boolean
DoublePredicate boolean test(double value) 一個(gè)參數(shù) boolean
IntPredicate boolean test(int value) 一個(gè)參數(shù) boolean

接口 方法 參數(shù) 返回值
Supplier T get() 無(wú) T
BooleanSupplier boolean getAsBoolean() 無(wú) boolean
DoubleSupplier double getAsDouble() 無(wú) boolean
IntSupplier int getAsInt() 無(wú) boolean

接口 方法 參數(shù) 返回值
Consumer void accept(T t) 一個(gè)參數(shù) void
BiConsumer void accept(T t, U u) 兩個(gè)參數(shù) void
DoubleConsumer void accept(double value) 一個(gè)參數(shù) void
IntConsumer void accept(int value) 一個(gè)參數(shù) void
ObjDoubleConsumer void accept(T t, double value) 兩參數(shù) void

上面為Java 8 中函數(shù)式接口列表

這里給出一份較為全的函數(shù)式接口與描述符對(duì)應(yīng)的接口聲明列表:

函數(shù)式接口 函數(shù)描述符
Predicate (T) -> boolean
Consumer (T) -> void
Function< T, R > (T) -> R
Supplier () -> T
UnaryOperator (T) -> T
BinaryOperator (T, T) -> T
BiPredicate (L, R) -> boolean
BiConsumer (T, U) -> void
BiFunction (T, U) -> R

如果同學(xué)們需要的函數(shù)式接口沒(méi)有被覆蓋陨享,可以根據(jù)JDK中的聲明來(lái)編寫適合自己使用的函數(shù)式接口聲明葱淳。

Optional

用于簡(jiǎn)化Java中對(duì)空值的判斷處理,以防止出現(xiàn)各種空指針異常抛姑。
Optional實(shí)際上是對(duì)一個(gè)變量進(jìn)行封裝赞厕,它包含有一個(gè)屬性value,實(shí)際上就是這個(gè)變量的值定硝。

Optional對(duì)象創(chuàng)建

它的構(gòu)造函數(shù)都是private類型的皿桑,因此要初始化一個(gè)Optional的對(duì)象無(wú)法通過(guò)其構(gòu)造函數(shù)進(jìn)行創(chuàng)建。它提供了一系列的靜態(tài)方法用于構(gòu)建Optional對(duì)象:

empty

用于創(chuàng)建一個(gè)空的Optional對(duì)象;其value屬性為Null诲侮。
如:

Optional o = Optional.empty();

of

根據(jù)傳入的值構(gòu)建一個(gè)Optional對(duì)象;
傳入的值必須是非空值镀虐,否則如果傳入的值為空值,則會(huì)拋出空指針異常沟绪。
使用:

o = Optional.of("test"); 

ofNullable

根據(jù)傳入值構(gòu)建一個(gè)Optional對(duì)象
傳入的值可以是空值刮便,如果傳入的值是空值,則與empty返回的結(jié)果是一樣的绽慈。

方法

Optional包含以下方法:

方法名 說(shuō)明
get 獲取Value的值诺核,如果Value值是空值,則會(huì)拋出NoSuchElementException異常久信;因此返回的Value值無(wú)需再做空值判斷,只要沒(méi)有拋出異常漓摩,都會(huì)是非空值裙士。
isPresent Value是否為空值的判斷;
ifPresent 當(dāng)Value不為空時(shí)管毙,執(zhí)行傳入的Consumer腿椎;
ifPresentOrElse Value不為空時(shí),執(zhí)行傳入的Consumer夭咬;否則執(zhí)行傳入的Runnable對(duì)象啃炸;
filter 當(dāng)Value為空或者傳入的Predicate對(duì)象調(diào)用test(value)返回False時(shí),返回Empty對(duì)象卓舵;否則返回當(dāng)前的Optional對(duì)象
map 一對(duì)一轉(zhuǎn)換:當(dāng)Value為空時(shí)返回Empty對(duì)象南用,否則返回傳入的Function執(zhí)行apply(value)后的結(jié)果組裝的Optional對(duì)象;
flatMap 一對(duì)多轉(zhuǎn)換:當(dāng)Value為空時(shí)返回Empty對(duì)象掏湾,否則傳入的Function執(zhí)行apply(value)后返回的結(jié)果(其返回結(jié)果直接是Optional對(duì)象)
or 如果Value不為空裹虫,則返回當(dāng)前的Optional對(duì)象;否則融击,返回傳入的Supplier生成的Optional對(duì)象筑公;
stream 如果Value為空,返回Stream對(duì)象的Empty值尊浪;否則返回 Stream.of(value)的Stream對(duì)象匣屡;
orElse Value不為空則返回Value,否則返回傳入的值拇涤;
orElseGet Value不為空則返回Value捣作,否則返回傳入的Supplier生成的值;
orElseThrow Value不為空則返回Value工育,否則拋出Supplier中生成的異常對(duì)象虾宇;

使用場(chǎng)景

常用的使用場(chǎng)景如下:

判斷結(jié)果不為空后使用

如某個(gè)函數(shù)可能會(huì)返回空值,以往的做法:

String s = test();
if (null != s) {
    System.out.println(s);
}

現(xiàn)在的寫法就可以是:

Optional<String> s = Optional.ofNullable(test());
s.ifPresent(System.out::println);

乍一看代碼復(fù)雜度上差不多甚至是略有提升如绸;那為什么要這么做呢嘱朽?
一般情況下旭贬,我們?cè)谑褂媚骋粋€(gè)函數(shù)返回值時(shí),要做的第一步就是去分析這個(gè)函數(shù)是否會(huì)返回空值搪泳;如果沒(méi)有進(jìn)行分析或者分析的結(jié)果出現(xiàn)偏差稀轨,導(dǎo)致函數(shù)會(huì)拋出空值而沒(méi)有做檢測(cè),那么就會(huì)相應(yīng)的拋出空指針異常岸军!
而有了Optional后奋刽,在我們不確定時(shí)就可以不用去做這個(gè)檢測(cè)了,所有的檢測(cè)Optional對(duì)象都幫忙我們完成艰赞,我們要做的就是按上述方式去處理佣谐。

變量為空時(shí)提供默認(rèn)值

如要判斷某個(gè)變量為空時(shí)使用提供的值,然后再針對(duì)這個(gè)變量做某種運(yùn)算方妖;
以往做法:

if (null == s) {
    s = "test";
}
System.out.println(s);

現(xiàn)在的做法:

Optional<String> o = Optional.ofNullable(s);
System.out.println(o.orElse("test"));

變量為空時(shí)拋出異常狭魂,否則使用

以往寫法:

if (null == s) {
    throw new Exception("test");
}
System.out.println(s);

現(xiàn)在寫法:

Optional<String> o = Optional.ofNullable(s);
System.out.println(o.orElseThrow(()->new Exception("test")));

github源碼地址

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市党觅,隨后出現(xiàn)的幾起案子雌澄,更是在濱河造成了極大的恐慌,老刑警劉巖杯瞻,帶你破解...
    沈念sama閱讀 206,968評(píng)論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件镐牺,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡魁莉,警方通過(guò)查閱死者的電腦和手機(jī)睬涧,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)旗唁,“玉大人宙地,你說(shuō)我怎么就攤上這事∧嫫ぃ” “怎么了宅粥?”我有些...
    開封第一講書人閱讀 153,220評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)电谣。 經(jīng)常有香客問(wèn)我秽梅,道長(zhǎng),這世上最難降的妖魔是什么剿牺? 我笑而不...
    開封第一講書人閱讀 55,416評(píng)論 1 279
  • 正文 為了忘掉前任企垦,我火速辦了婚禮,結(jié)果婚禮上晒来,老公的妹妹穿的比我還像新娘钞诡。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,425評(píng)論 5 374
  • 文/花漫 我一把揭開白布荧降。 她就那樣靜靜地躺著接箫,像睡著了一般。 火紅的嫁衣襯著肌膚如雪朵诫。 梳的紋絲不亂的頭發(fā)上辛友,一...
    開封第一講書人閱讀 49,144評(píng)論 1 285
  • 那天,我揣著相機(jī)與錄音剪返,去河邊找鬼废累。 笑死,一個(gè)胖子當(dāng)著我的面吹牛脱盲,可吹牛的內(nèi)容都是我干的邑滨。 我是一名探鬼主播,決...
    沈念sama閱讀 38,432評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼钱反,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼驼修!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起诈铛,我...
    開封第一講書人閱讀 37,088評(píng)論 0 261
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎墨礁,沒(méi)想到半個(gè)月后幢竹,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,586評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡恩静,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,028評(píng)論 2 325
  • 正文 我和宋清朗相戀三年焕毫,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片驶乾。...
    茶點(diǎn)故事閱讀 38,137評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡邑飒,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出级乐,到底是詐尸還是另有隱情疙咸,我是刑警寧澤,帶...
    沈念sama閱讀 33,783評(píng)論 4 324
  • 正文 年R本政府宣布风科,位于F島的核電站撒轮,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏贼穆。R本人自食惡果不足惜题山,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,343評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望故痊。 院中可真熱鬧顶瞳,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,333評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至抡柿,卻和暖如春舔琅,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背洲劣。 一陣腳步聲響...
    開封第一講書人閱讀 31,559評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工备蚓, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人囱稽。 一個(gè)月前我還...
    沈念sama閱讀 45,595評(píng)論 2 355
  • 正文 我出身青樓郊尝,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親战惊。 傳聞我的和親對(duì)象是個(gè)殘疾皇子流昏,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,901評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容