java8新特性lambda表達式

Lambda表達式

簡介

Lambda表達式能夠取代大部分匿名內部類,能夠優化代碼結構。java

能夠取代匿名內部類?什麼意思呢?數組

在之前若是咱們須要對集合排序,咱們是這樣作:app

Integer[] arr= {3,2,1};
Arrays.sort(arr, new Comparator<Integer>() {

	@Override
	public int compare(Integer o1, Integer o2) {
		return o1-o2;
	}
});
System.out.println(Arrays.toString(arr));

使用Arrays類提供的sort方法傳入一個指定排序規則的Comparator,若是咱們使用匿名內部類的話,能夠看到整個內部類中只用return o1-o2;語句是有用的,其餘的都是多餘的,爲了這一句話咱們多寫了不少代碼。那麼,有了Lambda表達式後咱們就能夠很輕鬆的解決這個問題了。ide

java8中新增了Lambda表達式,如今咱們能夠這樣作:函數

Integer[] arr= {3,2,1};
Arrays.sort(arr, (x,y)->x-y);
System.out.println(Arrays.toString(arr));

那麼Lambda是如何實現的呢?咱們知道sort方法須要傳入一個Comparator,而Comparator是一個接口,那麼咱們來看看Comparator接口是怎樣定義的:優化

@FunctionalInterface
public interface Comparator<T> {

Comparator可以支持Lambda表達式的祕密就是類上標註的@FunctionalInterface註解,被這個註解標註的接口只能有一個抽象方法,咱們知道咱們寫Lambda表達式時並無指定方法,那麼當使用Lambda表達式時咱們從新的就是這個方法。this

Lambda表達式基本語法

語法形式爲 () -> {},其中 () 用來描述參數列表,{} 用來描述方法體,-> 爲 lambda運算符code

  1. 接口無參無返回對象

    @FunctionalInterface
    interface NoParamNoReturn {
    	void lambda();
    }
    
    @Test
    public void test() {
      NoParamNoReturn noParamNoReturn=()->{System.out.println("No param No return");};
      noParamNoReturn.lambda();
    }
    //若是方法內只有一個語句那麼{}能夠省略
    @Test
    public void test() {
      NoParamNoReturn noParamNoReturn=()->System.out.println("No param No return");
      noParamNoReturn.lambda();
    }
  2. 接口有一個或多個參數無返回排序

    @FunctionalInterface
    interface OneParamNoReturn{
    	void lambda(int x);
    }
    @Test
    public void test() {
      OneParamNoReturn oneParamNoReturn=(int x)->System.out.println(x);
      oneParamNoReturn.lambda(10);
    }
    //若是方法只有一個參數那麼()能夠省略
    //方法參數的類型也能夠省略,編譯器會根據方法參數類型推斷
    @Test
    public void test() {
      OneParamNoReturn oneParamNoReturn=x->System.out.println(x);
      oneParamNoReturn.lambda(10);
    }
  3. 接口無參數有返回值

    @FunctionalInterface
    interface NoParamHasReturn{
    	int lambda();
    }
    @Test
    public void test() {
      NoParamHasReturn noParamHasReturn=()->{return 10;};
      noParamHasReturn.lambda();
    }
    //當方法只有return語句時,能夠省略{}和return
    @Test
    public void test() {
      NoParamHasReturn noParamHasReturn=()->10;
      noParamHasReturn.lambda();
    }
  4. 接口有一個或多個參數有返回值

    @FunctionalInterface
    interface HasParamHasReturn{
    	int lambda(int x,int y);
    }
    @Test
    public void test() {
      HasParamHasReturn hasParamHasReturn=(x,y)->x+y;
      hasParamHasReturn.lambda(10, 20);
    }

    Lambda表達式引用方法

    咱們可使用lambda表達式把接口快速指向一個已經實現了的方法

    語法:方法歸屬者::方法名 靜態方法的歸屬者爲類名,普通方法歸屬者爲對象

    @FunctionalInterface
    interface HasParamHasReturn{
    	int lambda(int x,int y);
    }
    public class LambdaTest{
      public int add(int x,int y) {
        return x+y;
      }
    //lambda表達式指向對象方法
      @Test public void test() {
        LambdaTest lt=new LambdaTest();
        HasParamHasReturn hasParamHasReturn=lt::add;
        hasParamHasReturn.lambda(10, 20);
      }
      
      public static int sub(int x,int y) {
        return x-y;
      }
    
      //lambda表達式引用靜態方法
      @Test
      public void test12() {
        HasParamHasReturn hasParamHasReturn=LambdaTest::sub;
        hasParamHasReturn.lambda(10, 20);
      } 
    }
    
    //類名::實例方法 特殊狀況,只有當參數列表爲一個參數而且這個參數是方法調用者或多個參數而且第一個參數是調用者其餘參數是參數列表
    @Test
    public void test() {
      BiPredicate<String,String> bp=(x,y)->x.equals(y);
      //也能夠簡寫爲
      BiPredicate<String,String> bp2=String::equals;
    }

    lambda表達式引用構造函數建立對象

    語法:類名::new;

    class User{
    	String name;
    	int age;
    	
    	public User() {}
    	
    	public User(String name, int age) {
    		super();
    		this.name = name;
    		this.age = age;
    	}
    	
    }
    
    @FunctionalInterface
    interface UserCreatorBlankConstruct{
    	User getUser();
    }
    
    //使用lambda表達式引用構造器
    @Test
    public void test() {
      UserCreatorBlankConstruct creator1=User::new;
      creator1.getUser();
    }
    
    @FunctionalInterface
    interface UserCreatorParamConstruct{
    	User getUser(String name,int age);
    }
    
    @Test
    public void test13() {
      UserCreatorParamConstruct creator2=User::new;
      creator2.getUser("tom", 20);
    }

    java8爲咱們提供了4個核心的函數式接口

    1. 消費型接口
@FunctionalInterface
public interface Consumer<T> {
void accept(T t);
}
  1. 供給型接口

    @FunctionalInterface
    public interface Supplier<T> {
        T get();
    }
  2. 函數型接口

    @FunctionalInterface
    public interface Function<T, R> {
        R apply(T t);
    }
  3. 斷言型接口

    @FunctionalInterface
    public interface Predicate<T> {
        boolean test(T t);
    }

    怎麼用呢?

    遍歷數組

    @Test
    	public void test() {
    		List<Integer> list=new ArrayList<>();
    		list.add(1);
    		list.add(3);
    		list.add(5);
    		list.add(7);
    		list.forEach(System.out::println);
    	}

    建立對象

    @Test
    public void test() {
      Supplier<User> supplier=User::new;
      User user = supplier.get();
    }

    去除先後空格並轉爲大寫

    @Test
    public void test16() {
      Function<String, String> fun=s->{s=s.trim();s=s.toUpperCase();return s;};
      String apply = fun.apply(" abCd");
    }

    刪除集合元素

    @Test
    public void test() {
      List<User> list=new ArrayList<>();
      list.add(new User("tom",20));
      list.add(new User("jack",18));
      list.add(new User("marry",22));
    
      list.removeIf(e->e.getName()=="jack");
    }

    那若是咱們要用內置函數式接口建立對象,怎麼作呢?

    @Test
    public void test() {
      Supplier<User> supplier=User::new;
      User user = supplier.get();
      System.out.println(user);//User [name=null, age=0]
    }

    那到底使用的是哪一個構造器呢?經過輸出建立的對象能夠發現調用的是無參構造器,即調用構造器參數個數對應Supplier中get方法的參數個數的構造器

    那麼問題又來了,若是咱們要使用兩個參數的構造器,那Supplier也不行啊,Function的apply方法也只有一個參數,怎麼辦?那咱們去java.util.function包下找找有沒有能夠用的接口

    @FunctionalInterface
    public interface BiFunction<T, U, R> {
        R apply(T t, U u);
    }
    //使用內置函數式接口建立對象
    @Test
    public void test20() {
      BiFunction<String,Integer,User> biFunction=User::new;
      User user = biFunction.apply("tom", 20);
      System.out.println(user);//User [name=tom, age=20]
    }

    四個基本接口參數個數不夠用也能夠相似的去java.util.function包下找找有沒有申明好的函數式接口

最後一個問題,我發現

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

遍歷List時,使用的forEach的參數Consumer的accept方法是這麼寫的,我第一個想到的是,out是System的一個內部類,可是當我點進去,發現是這樣的

public final static PrintStream out = null;

out是一個靜態的成員變量,那個人理解就是System.out實際上是一個對象

這樣System.out::println的寫法其實也就是 對象名::方法名

我是這樣理解的,若是錯了還請賜教!

相關文章
相關標籤/搜索