lambda表達式:JAVA8 函數式接口與Lambda表達式初步

在這裏插入圖片描述

概要

Java Lambda表達式是一種匿名函數,他是沒有聲明的方法,即沒有訪問修飾符,返回值聲明和名字

作用

1. 傳遞行爲,而不僅僅是值
2. 提升抽象層次
3. API重用性更好
4. 更加靈活

Lambda結構

  • 一個Lambda表達式可以有零個或多個參數
  • 參數的類型既可以明確聲明,也可以根據上下文來推斷。例如:(int a)與(a)效果相同
  • 所有參數需包含在圓括號內,參數之間用逗號相隔。例如:(a,b)或者(int a,int b)或(String a,String b,float c)
  • 空圓括號代表參數集爲空。例如:()-> 42

Lambda 箭頭左側(參數)->箭頭右側(執行體) 當左側沒有參數時 左側括號是不能省略的
在這裏插入圖片描述

public class Test1 {

    public static void main(String[] args) {

        List<Integer> list = Arrays.asList(1, 3, 8);

        /*
      1.  @FunctionalInterface 凡是一個類加上該註解 都是函數式接口
        請注意,加了改接口可以使用
        lambda表達式、方法引用或構造函數引用。

      2.滿足以下規則 不然系統會給你生成一個錯誤信息
        1.類型是接口類型,而不是批註類型、枚舉或類
        2.滿足函數式接口要求
         */
        list.forEach(new Consumer<Integer>() {
            @Override
            public void accept(Integer integer) {
           		//我們可以將行爲理解爲對應函數式接口方法的實現方法
                System.out.println(integer);
            }
        });

    }
}

關於函數式接口
1.如果一個接口只有一個抽象方法,那麼改接口就是一個函數式接口
2.如果我們在某個接口聲明@FunctionalInterface註解,那麼編譯就會暗中函數式接口定義來要求該接口
3.如果某個接口只有一個抽象方法,但我們並沒有該接口聲明@FunctionalInterface註解。那麼編譯器依舊會將該接口看做函數式接口。
4.注意,函數接口的實例可以用lambda表達式、方法引用或構造函數引用創建

Iterable

jdk1.5 ,實現該接口的類可以進行迭代 ,1.8以後集合都可以直接使用默認方法forEach方法進行迭代
(jdk8之後 接口可以寫默認方法和 static實現方法)

在這裏插入圖片描述

forEach

doc文檔
對Iterable的每個行爲執行給定的操作,直到處理完所有行爲或操作引發異常爲止。除非實現類另有指定,否則按迭代順序(如果指定了迭代順序)執行操作。操作引發的異常將取決於調用方
forEach改方法是將==行爲動作(函數 在java中一種特別的對象)==當做參數進行傳遞 在jdk1.8之前方法參數只能傳遞具體數據
在這裏插入圖片描述
實例1

public class Test1 {

    public static void main(String[] args) {

        List<Integer> list = Arrays.asList(1, 3, 8);

        /*
      1.  @FunctionalInterface 凡是一個類加上該註解 都是函數式接口
        請注意,加了改接口可以使用
        lambda表達式、方法引用或構造函數引用。

      2.滿足以下規則 不然系統會給你生成一個錯誤信息
        1.類型是接口類型,而不是批註類型、枚舉或類
        2.滿足函數式接口要求
         */
        list.forEach(new Consumer<Integer>() {
            @Override
            public void accept(Integer integer) {
                System.out.println(integer);
            }
        });

        list.forEach(x-> System.out.println(x));

        Consumer<Integer> s = x-> System.out.println(x);
    }
}

Consumer(重要)

doc文檔
代表着一個接受單個輸入參數且不返回結果的操作。與大多數其他函數式接口不同,消費者希望通過副作用(就是可能會去修改接收單個參數)進行操作。類似Test1中accept執行給定行爲具體操作給定的行爲參數即可

當調用該接口可以直接使用Lambda表達式操作 如accept方法 ( T x)->方法體
在這裏插入圖片描述
我們可以對流中元素的屬性進行修改

        Student student = new Student();
        List<Student> integers = Arrays.asList(student);
        List<Student> students = integers.stream().peek(x -> {
            x.setScore(90);
            x.setUsername("zhangsan");
        }).collect(Collectors.toList());
        System.out.println(students);
    }

項目中如何使用?

在這裏插入圖片描述
在這裏插入圖片描述
實例2

@FunctionalInterface
interface MyIterfece {

    void test();

//    void test2(String s);

    /*
        爲什麼函數式接口裏有兩個抽象方法 系統不報錯
        因爲 該接口的實現類 一定繼承於Object基類 基類存在toString()方法 無需實現類進行創建 所以函數式接口允許存在
        Object基類的抽象方法
     */
    @Override
    String toString();
}

class Test2 {

    public void meTest(MyIterfece myIterfece) {
        System.out.println("1");
        myIterfece.test();
        System.out.println("2");
    }

    public static void main(String[] args) {
        Test2 test2 = new Test2();
        test2.meTest(new MyIterfece() {
            @Override
            public void test() {
                System.out.println("mytest");
            }
        });
        /*
        當函數式接口裏的方法沒有參數時 ()不能省略
        因爲函數式接口只有一個抽象方法 所以方法名就不那麼重要
         */

        test2.meTest(() -> System.out.println("mytest"));

        System.out.println("====================================");

        //等同於MyIterfece接口實現類
        MyIterfece myIterfece = () -> System.out.println("mytest");

        System.out.println(myIterfece.getClass());
        System.out.println(myIterfece.getClass().getSuperclass());
        //打印該實現類實現的接口
        System.out.println(myIterfece.getClass().getInterfaces()[0]);
    }


}

打印結果

/*
1
mytest
2
1
mytest
2
====================================
MyIterfece接口的實現類
class com.shengsiyuan.jdk8.Test2$$Lambda$2/1603195447
實現類接口的父類
class java.lang.Object
實現類實現的接口
interface com.shengsiyuan.jdk8.MyIterfece

 */

傳統外部迭代:通過下標一個一個迭代出數據(for循環 增強for 迭代器)
內部迭代: 從內部以一個取出數據

例3:將集合英文變成大寫


public class Test3 {

    public static void main(String[] args) {

//        /*
//            對於lambda表達式來說 完全不用管函數式接口裏方法名是什麼,因爲其實通過上下文進行類型推斷
//            只需要關係返回值和參數即可
//         */
//        TheInterface theInterface = () -> {
//        };
//        System.out.println(theInterface.getClass().getInterfaces()[0]);
//
//        TheInterface2 theInterface2 = () -> {
//        };
//        System.out.println(theInterface2.getClass().getInterfaces()[0]);
//
//        //lambda表達式必須依附上下文定位到對應類型 如果沒有上下文就會報錯
////        ()->{};
//
//
//        new Thread(() -> System.out.println("thread")).start();

        //將集合英文變成大寫
        List<String> list = Arrays.asList("hello", "world", "hello world");

//        List<String> list2 = Lists.newArrayList();
//        list.forEach(x -> list2.add(x.toUpperCase()));
//        list2.forEach(x -> System.out.println(x));

        /*
            採用stream流的方式來編寫
            map 映射的意思就是將一個值映射爲另外一個值
            Stream方法中也有個forEach方法作用類似Iterable接口的forEach
         */
//        list.stream().map(x -> x.toUpperCase()).forEach(y -> System.out.println(y));

        /*
            採用方法引用的方式
            x -> x.toUpperCase() 等價於 String::toUpperCase
         */
        list.stream().map(String::toUpperCase).forEach(System.out::println);

        /*
           Function<String,String>
           輸入是調用toString lambda對象的第一個參數 x(也就是String對象) (  x -> x.toUpperCase() 等價於 String::toUpperCase)
           輸出是toString方法返回的值

         */
        Function<String, String> function = String::toString;
        System.out.println(function.getClass().getInterfaces()[0]);


    }


    @FunctionalInterface
    interface TheInterface {

        void myMethod();
    }

    @FunctionalInterface
    interface TheInterface2 {

        void myMethod2();
    }
}

例4

public class StringComparator {

    public static void main(String[] args) {
        /*
            集合倒敘排序
         */
        List<String> list = Arrays.asList("zhangsan", "zhaojie", "maliu");

        Collections.sort(list, (x, y) -> x.compareTo(y));
        System.out.println(list);

    }
}

在例三中stream().map(Function<? super T, ? extends R> mapper)
在這裏插入圖片描述

Function(重要)

 		Function<String,String>
           輸入:是調用toString lambda對象的第一個參數 x(也就是String對象) (  x -> 			  x.toUpperCase() 等價於 String::toUpperCase)
           返回:是toString方法返回的值

         */
        Function<String, String> function = String::toString;
        System.out.println(function.getClass().getInterfaces()[0]);
		//map參數就是Function<T,R> 此時 將x當做參數,x.toUpperCase() 當做值返回
 		list.stream().map(x -> x.toUpperCase()).forEach(y -> System.out.println(y));

在這裏插入圖片描述
例5

public class FunctionTest {

    public static void main(String[] args) {

        FunctionTest functionTest = new FunctionTest();
  
        System.out.println(functionTest.function(5, x -> x * 2));

        /*
            x -> x +2 是一個行爲  當調用方法時他將 x +2 這種行爲動作傳過去
            執行到function.apply(a)時 此時纔會執行改行爲 執行完返回對應數據
            此時行爲是用的時候傳過去對應的行爲(加減乘除) 
            
         */
        System.out.println(functionTest.function(10, x -> x + 2));
        System.out.println(functionTest.function(10, x -> x * x));


        System.out.println(functionTest.function2(10, x -> x + "你好啊!"));

        //傳統方式
        System.out.println(functionTest.method(2));
    }

    //接受一個參數和一個行爲
    public int function(int a, Function<Integer, Integer> function) {
        Integer apply = function.apply(a);
        return apply;
    }

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

    /*
        傳統方法,我們需要提前將行爲定義好 再去調用對應的方法
        在運行之前行爲都是定義好的做不到靈活多變

     */
    public int method(Integer a) {
        return a * 5;
    }

    public int method2(Integer a) {
        return a + 2;
    }

    public int method3(Integer a) {
        return a * a;
    }
}
10
12
10你好啊!

Function中兩個默認方法 一個靜態方法

	/*
		總是返回始終返回調用方
		其輸入參數的函數
	*/
	 static <T> Function<T, T> identity() {
	        return t -> t;
	    }
	/*
		組合函數 將傳過來的Function行爲apply執行結果 當做第二個Function的行爲的執行參數 
		返回apply結果
	*/
    default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
        Objects.requireNonNull(before);
        return (V v) -> apply(before.apply(v));//返回結果
    }

    /**
    	組合函數 與上面相反 是將當前的Function行爲apply執行結果 當做傳過來Function的行爲的執行參數
    	返回apply結果
     */
    default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
        Objects.requireNonNull(after);
        //t = 當前Function調用andThen的對象行爲 
        return (T t) -> after.apply(apply(t));
    }

例6

	
        Function<String, String> function3 = y -> y + "1";


        Function<String, String> function2 = x -> x + "nihao";
        //將String對象 傳過去
        Function<String, String> andThen = function2.andThen(function3);
        Function<String, String> compose = function2.compose(function3);
		//返回結果根據用戶傳過來的的行爲決定 ,參數再去執行對應的行爲
        String andThenapply = andThen.apply("22");
        System.out.println(andThenapply); 

        System.out.println("=======================================");

        String composeapply = compose.apply("22");
        System.out.println(composeapply);

打印結果

22nihao1 
=======================================
221nihao//先執行y -> y + "1".apply("22") 得到的結果221 在執行 x -> x + "nihao".apply(221)
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章