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的写法其实也就是 对象名::方法名

我是这样理解的,若是错了还请赐教!

相关文章
相关标签/搜索