Java8新特性之Lambda表達式

概述

        lambda表示數學符號「λ」,計算機領域中λ表明「λ演算」,表達了計算機中最基本的概念:「調用」和「置換」。html

全局model代碼:Employee.javajava

package www.muzi.com.model;

/**
 * Date:2017/3/3 12:44
 */
public class Employee {
	private String name;
	private int age;

	public Employee() {
	}

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

	public Employee(String name) {
		this.name = name;
	}

	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 "Employee{" +
				"name='" + name + '\'' +
				", age=" + age +
				'}';
	}
}

Lambda表達式特色

  1.  Lambda表達式是一段能夠傳遞的代碼塊,能夠被執行一次或屢次。
  2. 減小編程代碼量
  3. Lambda表達式能夠轉換爲函數式接口
  4. Lambda表達式能夠在閉包做用域中有效的訪問final變量

Lambda表達式語法

        Java8中引入了一個新的操做符"->",該操做符稱做箭頭操做符或者Lambda操做符,箭頭操做符將Lambda表達式拆分爲兩部分:編程

左側:Lambda 表達式參數列表
右側:Lambda 表達式中須要執行的功能,及Lambda體api

變量做用域

  1. Lambda還包括自由變量的值,這裏的「自由」指的是那些不是參數而且沒有在代碼中定義的變量。
    public void repeatMessage(String content, int count) {
    		Runnable r = () -> {
    			for (int i = 0; i < count; i++) {
    				System.out.println(i + "-" + content);
    				count++;//錯誤,不能更改已捕獲變量的值
    			}
    		};
    		r.run();
    	}
    
    	@Test
    	public void test8() {
    		repeatMessage("測試", 10);
    	}

     

  2. Lambda表達式的方法體與嵌套代碼塊有着相同的做用域。所以它也適合一樣的命名衝突和屏蔽規則。在Lambda表達式中不容許聲明一個與局部變量同名的參數或者局部變量。
    @Test
    	public void test9(){
    		//嵌套臺代碼塊
    		String ss = "";
    		{
    			String ss="dd";
    		}
    		//Lambda
    		Comparator<String> com = (ss, yy) -> Integer.compare(ss.length(), yy.length());
    		System.out.println(ss);
    	}

     

語法格式一:無參數,無返回值  () -> System.out.println("Hello Lambda");數組

@Test
	public void test1(){
		int num = 0; //jdk1.7以前,必須是final的
		Runnable r = new Runnable() {
			@Override
			public void run() {
				System.out.println("Hello World!" + num);
			}
		};
		r.run();
		System.out.println("------------------");
		Runnable r1 = () -> System.out.println("Hello Lambda!" + num);
		r1.run();
	}

語法格式二:有一個參數,無返回值  (x) -> System.out.println(x);閉包

語法格式三:當只有一個參數時,小括號能夠省略不寫  x -> System.out.println(x);oracle

@Test
	public void test2(){
		Consumer<String> con = (x) -> System.out.println(x);
		Consumer<String> con1 = x -> System.out.println(x);
		con.accept("測試Lambda表達式!");
	}

語法格式四:當有兩個或以上參數時,有返回值,而且Lambda中有多條語句
(x, y) -> {
             System.out.println("函數式接口");
             return Integer.compare(x, y);
 };app

@Test
	public void test3(){
		Comparator<Integer> com = (x, y) -> {
			System.out.println("函數式接口");
			return Integer.compare(x, y);
		};
		System.out.println(com.compare(2, 3));
	}

語法格式五:當有兩個或以上參數時,有返回值,Lambda中有一條語句,大括號和return能夠省略不寫dom

(x, y) -> Integer.compare(x, y);ide

@Test
	public void test4(){
		Comparator<Integer> com = (x, y) -> Integer.compare(x, y);
		System.out.println(com.compare(2, 3));
	}


語法格式六:Lambda參數列表的數據類型能夠省略不寫,由於JVM編譯器能夠經過上下文推斷出,數據類型,即「類型推斷」
(Integer x, Integer y) -> Integer.compare(x, y);

@Test
	public void test5(){
		Comparator<Integer> com = (Integer x, Integer y) -> Integer.compare(x, y);
		System.out.println(com.compare(2, 3));
		//類型推斷舉例
		List<String> list = new ArrayList<>();//jdk1.7中new ArrayList<String>()必須帶類型,1.8版本不須要帶類型
	}

Lambda表達式須要"函數式接口"的支持

函數式接口:接口中只有一個抽象方法的接口,成爲函數式接口。
可使用註解@FunctionalInterface修飾,能夠檢查是不是函數式接口。

函數式接口源代碼:

package www.muzi.com;

/**
 * Date:2017/3/6 18:02
 */
@FunctionalInterface
public interface MyFun<T> {
	public Integer getValue(Integer num);
}
//需求:對一個數進行運算
	@Test
	public void test6(){
		Integer num = operation(100, x -> x * x);
		System.out.println("num = " + num);

		Integer num1 = operation(200, y -> y + 200);
		System.out.println("num1 = " + num1);
	}

	public Integer operation(Integer num, MyFun<Integer> mf){
		return mf.getValue(num);
	}

由於目前在寫Lambda表達式時,老是須要去寫一個函數式接口。這樣其實也挺繁瑣,其實Java8已經替咱們內置了許多函數式接口,接下來介紹Java8內置四大函數式接口。

Java8內置四大函數式接口

       在Java8中添加了許多內置的函數式接口:

        

    這裏就展現如下四中函數式接口,其餘函數式接口看java8文檔:

http://docs.oracle.com/javase/8/docs/api/index.html

  1. Consumer<T> : 消費型接口
    //Consumer<T> : 消費型接口
    	@Test
    	public void test1(){
    		happy(10000, m -> System.out.println("訂餐消費" + m + "元"));
    	}
    
    	public void happy(double money, Consumer<Double> con){
    		con.accept(money);
    	};

     

  2. Supplier<T> : 供給型接口
    //Supplier<T> : 供給型接口
    	@Test
    	public void test2(){
    		List<Integer> numList = getNumberList(10, () -> (int)(Math.random() * 100));
    		for (Integer n : numList) {
    			System.out.println(n);
    		}
    	}
    
    	//需求:產生指定個數的整數,並放到集合中
    	public List<Integer> getNumberList(int num, Supplier<Integer> sup){
    		List<Integer> list = new ArrayList<>();
    		for (int i = 0; i < num; i++) {
    			Integer n =  sup.get();
    			list.add(n);
    		}
    		return list;
    	};

     

  3. Function<T, R> : 函數型接口
    //Function<T, R> : 函數型接口
    	@Test
    	public void test3(){
    		String str = strHandler("測試函數式接口  ", x -> x.trim());
    		System.out.println(str + "--");
    	}
    
    	//需求:處理字符串
    	public String strHandler(String str, Function<String, String> fun){
    		return fun.apply(str);
    	}

     

方法引用

       方法引用:若 Lambda 體中的功能,已經有方法提供了實現,可使用方法引用(能夠將方法引用理解爲 Lambda 表達式的另一種表現形式)。

      ::操做符將方法名和對象或類的名字分隔開來,主要介紹常使用的三種方法引用方式。

  1. 對象的引用 :: 實例方法名
    //對象的引用 :: 實例方法名
    	@Test
    	public void test1(){
    		Consumer<Integer> con = (x) -> System.out.println(x);
    
    		//使用方法引用實現
    		PrintStream ps = System.out;
    		Consumer<Integer> con1 = ps::println;
    
    		Consumer<Integer> con2 = System.out::println;
    		con2.accept(1234);
    	}
    
    	@Test
    	public void test2(){
    		Employee employee = new Employee("木子",23);
    		Supplier<String> sup = employee:: getName;
    		String name = sup.get();
    		System.out.println(name);
    	}

     

  2. 類名 :: 靜態方法名
    //類名 :: 靜態方法名
    	@Test
    	public void test3(){
    		Comparator<Integer> com = (x, y) -> Integer.compare(x, y);
    
    		//方法引用
            //方法引用所引用的方法的參數列表與返回值類型,須要與函數式接口中抽象方法的參數列表和返回值類型保持一致!
    		Comparator<Integer> com1 = Integer:: compare;
    		int result = com1.compare(3, 5);
    		System.out.println(result);
    	}

     

  3. 類名 :: 實例方法名
    //類名 :: 實例方法名
    	@Test
    	public void test4(){
    		BiPredicate<String, String> bp = (x, y) -> x.equals(y);
    		//若Lambda 的參數列表的第一個參數,是實例方法的調用者,第二個參數(或無參)是實例方法的參數時,格式: ClassName::MethodName
    		BiPredicate<String, String> bp1 = String:: equals;
    		boolean result = bp1.test("as", "as");
    		System.out.println(result);
    	}

    注意:
           ①方法引用所引用的方法的參數列表與返回值類型,須要與函數式接口中抽象方法的參數列表和返回值類型保持一致!
          ②若Lambda 的參數列表的第一個參數,是實例方法的調用者,第二個參數(或無參)是實例方法的參數時,格式: ClassName::MethodName

構造器引用

格式:  ClassName :: new  

//構造器引用
	@Test
	public void test5(){
		Supplier<Employee> sup = () -> new Employee();
		//構造器引用,自動匹配對應的構造器
		Supplier<Employee> sup1 = Employee::new;//由於沒有傳參數,這裏的new引用的是無參構造器
		Employee employee = sup1.get();
		System.out.println(employee); //Employee{name='null', age=0}
	}

	@Test
	public void test6(){
		Function<String, Employee> fun = (x) -> new Employee(x);

		Function<String, Employee> fun1 = Employee::new;//這裏引用的是一個參數的構造器
		System.out.println(fun1.apply("haha"));//Employee{name='haha', age=0}
	}

構造器引用 : 構造器的參數列表,須要與函數式接口中參數列表保持一致!

數組引用

格式: Type :: new

//數組引用
	@Test
	public void test7(){
		Function<Integer, String[]> fun = (x) -> new String[x];
		String[] strs = fun.apply(10);
		System.out.println(strs.length);  //10

		Function<Integer, String[]> fun1 = String[]::new;
		String[] strs1 = fun.apply(15);
		System.out.println(strs1.length);  //15
	}
相關文章
相關標籤/搜索