java lambda表达式

304 阅读5分钟

lambda表达式

简介

lambda表达式是java1.8的新特性,他的本质就是java的一个语法糖,由编译器推断并帮你转换包装为常规的代码,因此你可以使用更少的代码来实现同样的功能

在android项目中使用java8

1.在app build.gradle 增加如下代码

android {
  ...

  defaultConfig {

    ...

    jackOptions {
      enabled true
    }

  }

  compileOptions {
    sourceCompatibility JavaVersion.VERSION_1_8
    targetCompatibility JavaVersion.VERSION_1_8
  }

}

lambda表达式基本形式

简单的说一个lambda表达式由 (参数列表, ->箭头符号, 函数体)这三部分组成下面我们说一些简单的lambda表达式. ()内部是参数列表,箭头符号->是参数列表与函数体的连接符,{}用于描述函数体

  1. 参数为空函数体为空(无返回值)
()->{}
  1. 有一个参数(无返回值)
(v)->{}
//当只有一个参数时参数可以去掉小括号简化成如下格式
 v->{}
  1. 两个参数(无返回值)
(k,v)->{}
  1. 参数是i,j(返回值是他们的和)
(int i,int j)->i+j
//可简化为
(i,j)->i+j
  1. 不需要参数(返回值是10)
()->10

注意:

使用Lambda表达式必须满足只有一个待实现方法这个规则,否则就不能使用Lamda表达式进行代替

lambda表达式在android中的应用

  1. lambda表达式用于view点击事件
//非lambda表达式
btn.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            Toast.makeText(MainActivity.this,"我被点击了",Toast.LENGTH_LONG).show()
        }
});
//lambda表达式设置点击事件
btn.setOnClickListener(v->Toast.makeText(MainActivity.this,"我被点击了",Toast.LENGTH_LONG).show());
  1. lambda表达式用于创建新线程
//非lambda表达式创建新线程
new Thread(new Runnable() {
    @Override
    public void run() {
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                Toast.makeText(MainActivity.this,"我通过新线程弹出",Toast.LENGTH_LONG).show();
            }
        });
    }
}).start();
//lambda表达式
new Thread(()->runOnUiThread(()->Toast.makeText(MainActivity.this,"我通过新线程弹出",Toast.LENGTH_LONG).show())).start();
  1. lambda表达式用于给listView设置OnItemClickListener点击事件
//非lambda
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
        @Override
        public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
            System.out.println("您点击了postion="+position);
        }
});
//lambda
listView.setOnItemClickListener((parent,view,position,id)->System.out.println("您点击了postion="+position));

自定义场景

1.打印年龄超过20的人

假如我们需要打印一个list中所有年龄大于20的人我们可能会这样写:

//定义人的基础bean
class Person{
    public int age;
}
//定义打印判断方法
public void printUserInfo(ArrayList<Person> arrayList,int age){
    for(Person person:arrayList){
        if(person.age>age){
            System.out.println(person.age);
        }
    }
}

//最后是方法调用
printUserInfo(arrayList,20);

2.打印年龄超过20小于30的人

假如我们的打印条件改了,现在需要打印大于20小于三十的人的信息,那么我们需要修改printUserInfo方法

public void printUserInfo(ArrayList<Person> arrayList,int minAge,int maxAge){
    for(Person person:arrayList){
        if(person.age>minAge&&person<maxAge){
            System.out.println(persion.age);
        }
    }
}

3.条件再改?

假如我们现在打印我们想要的任意条件的人员信息我们需要怎么办?

//定义一个判断是否满足条件的接口
interface IsPrint{
    boolean isPrint(int age);
}
//改造打印方法
public void printUserInfo(ArrayList<Person> arrayList,IsPrint isPrint){
    for(Person person:arrayList){
        if(isPrint.isPrint(person.age)){
            System.out.println(person.age);
        }
    }
}

//匿名内部类
printUserInfo(arrayList, new IsPrint() {
    @Override
    public boolean isPrint(int age) {
        return age>20&&age<30;
    }
});
//lambda表达式
printUserInfo(arrayList,age->age>20&&age<30);

lambda表达式与函数式接口

  • 什么是函数式接口?

    任何接口,如果只包含唯一 一个抽象方法,那么它就是一个FI,所以我们可以把任何只包含一个抽象方法的接口,叫做函数式接口.当然为了规范,java1.8后java给我们提供了@FunctionalInterface这个注解,我们可以用它来定义函数式接口.

所以我们可以把上面我们用到的的接口这样写:

//标准的函数式接口
@FunctionalInterface
interface IsPrint{
    boolean isPrint(int age);
}
  • 用lambda来替代函数式接口的匿名对象

    由于上文lambda表达式必须满足只有一个抽象方法这个规则,而函数式接口只能包含一个抽象方法.所以我们认为,在java所有能用函数式接口的地方我们都可以用lambda表达式来替代.

  • java8中官方提供的函数式接口类

    java的函数式接口被定义在 java.util.function 包中,在function包中官方给我们提供了大量的函数式接口类,下面我们来看一下Predicate接口是如何供外部使用的?

    //Predicate的接口定义
    public interface Predicate<T>{
        boolean test(T t);
    }

上面的代码给我们展示了java中一个简单的函数式接口,他接收一个泛型作为test方法的参数并返回布尔类型,我们可以在任何需要经过某些判断然后返回布尔类型的地方用这个接口代替.

  • 用函数式接口替代上面的

    刚刚我们看到的Predicate接口其实与我们上面写的IsPrint接口结构大致相同,就是IsPrint我们固定了isPrint方法中的参数是int类型并没有使用泛型,并且方法名和类名不同而已.Predicate完全可以替代上面的IsPrint接口,并且比他更加通用.这样我们就可以传进任何对象进test方法中进行判断.

  • 修改上面的例子改为由javasdk提供的函数式接口来进行lambda表达式的判断

//改造打印方法
public void printUserInfo(ArrayList<Person> arrayList,Predicate<Person> predicate){
    for(Person person:arrayList){
        if(predicate.test(person)){
            System.out.println(person.age);
        }
    }
}
//修改调用
printUserInfo(arrayList,age->age.age>20&&age.age<30);

注意

在运用lambda表达式时,由于类型无法退导.重载方法中可能会影响表达式的使用.

例:

方法一:
public void printUserInfo(ArrayList<Person> arrayList,IsPrint isPrint){
    for(Person person:arrayList){
        if(isPrint.isPrint(person.age)){
            System.out.println(person.age);
        }
    }
}
方法二:
public void printUserInfo(ArrayList<Person> arrayList,Predicate<Person> predicate){
    for(Person person:arrayList){
        if(predicate.test(person)){
            System.out.println(person.age);
        }
    }
}

分别调用方法一 和 方法二
//调用方法一
printUserInfo(arrayList, new IsPrint() {
    @Override
    public boolean isPrint(int age) {
        return age>20&&age<30;
    }
});
//调用方法二 
(此时报错,编译器 Ambiguous method call) 程序无法编译
printUserInfo(arrayList,person -> person.age>20&&person.age<30);


本节完