Android-Lambda表达式

参考资料:
https://mp.weixin.qq.com/s/ht3KIeG9-H9HtgF_nlm5cw

Lambda,中文名“兰布达”。是匿名函数的别名,Java8后开始引入Lambda表达式.而Android方面Android Studio 2.4 Preview 4 及其之后完全的支持lambda 表达式,如果是之前版本就需要借助插件和编译器了。下面以我们常见的点击事件为例开始讲解Lambda表达式,先看下面的代码:

TextView tv =findViewById(R.id.textView);
        tv.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

            }
        });

上面的代码是我们常见的简单的不能在简单的点击事件,我们把此代码转化为Lambda表达是看看效果,等等,我们上面说了Java8后开始引入Lambda表达式支持,Android Studio 2.4 Preview 4 及其之后完全的支持lambda 表达式,那我们只需要设置一下自己的Project引用的是JDK1.8即可,如图:

image.png

然后我们回到代码发现,建议开发者替换原有写法,改为lambda表达式:
image.png

我们按照要求选择一下,然后回车:
image.png

既可以看到代码变成了如下的样子:
image.png

我去,,第一眼感觉就是代码变少了。这其实也是Lambda表达式的优点:其对匿名内部类笨拙繁琐的代码的简化.lambda 表达式不仅对对象名进行隐匿,更完成了方法名的隐匿,展示了一个接口抽象方法最有价值的两点:参数列表和具体实现.
那么我们就来探讨监听事件是怎么通过Lambda表达式一步步的如此简洁的。

1.Lambda表达式的形式

Lambda表达式共有三种形式:函数式接口,方法引用,构造器引用。其中函数式接口是最基本的,其余两种形式都是基于它进行拓展。

1.1函数式接口

函数式接口指的是有且只有一个抽象方法的接口,比如我们上面的OnClickListener。lambda 表达式就是对这类接口的匿名内部类进行简化。基本形式如下:

( 参数列表... ) -> { 语句块... }

  • ok,那我们基于基本形式对setOnClickListener(new View.OnClickListener()){}做一下改变为:
 // 基本形式如下:( 参数列表... ) -> { 语句块... }
        tv.setOnClickListener((View v) -> {
        //doSomeThing.....
        });
  • 当参数只有一个时,参数列表两侧的圆括号也可省略
 //参数只有一个时(注意是只有一个时,两个时就正常写吧),参数列表两侧的圆括号也可省略
        tv.setOnClickListener(v -> {
            //doSomeThing.....
        });
  • 当语句块内的处理逻辑只有一句表达式时,其两侧的花括号也可省略
  tv.setOnClickListener(v -> Log.e(TAG, "花括号也可省略" ));

看到没,就是这个样子,就是这么变过来的,就是这么简单

  • 当只有一句去除花括号的表达式且接口方法需要返回值时,这个表达式不用(也不能)在表达式前加 return ,就可以当作返回语句。下面用 Java 的 Function 接口作为示例,这是一个用于转换类型的接口,在这里我们获取一个 User 对象的姓名字符串并返回:


    image.png

1.2方法引用形式

我们先来一个方法引用形式的分类,然后一个个讲解,方法应用形式有三种:

  • ClassName :: staticMethod
  • ClassName :: instanceMethod
  • object :: instanceMethod
  • 1.2.1 ClassName :: staticMethod
    直接调用某类的静态方法,并将接口方法参数传入。
    比如我们写一个例子看看,我们先定义一个判断字符是否为空的接口:
public interface IIsEmpty<T> {
    //判断是否为空的接口
    boolean isEmpty( T t);
}

我们知道TextUtils.isEmpty()可以判断非空,那我们就引用,然后在看其具体的实现:

 IIsEmpty<String> iIsEmpty = s1 -> TextUtils.isEmpty(s1);

我们在继续使用方法引用的形式继续简化这一段 lambda 表达式(也就是 ClassName :: staticMethod)

IIsEmpty<String> iIsEmpty = TextUtils::isEmpty;

方法引用形式就是当逻辑实现只有一句且调用了已存在的方法进行处理( this 和 super 的方法也可包括在内)时,对函数式接口形式的 lambda 表达式进行进一步的简化。传入引用方法的参数就是原接口方法的参数。

如果上面的你还没看懂,我们在来一个例子:

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

    public  static boolean doPredicate(int age, Predicate<Integer> predicate) {
        return predicate.test(age);
    }

   @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //判断输入的数字是否大于18
        boolean isAdult = doPredicate(20, x -> x >= 18);
        System.out.println(isAdult);
}

这下懂了没?
实际上上述例子中的接口是不用我们自己写的,jdk设计者已经为我们准备了java.util.function包:


image.png

我们前面写的Predicate函数式接口也是JDK种的一个实现,他们大致分为以下几类:


image.png
  • 1.2.2 object :: instanceMethod
    直接调用任意对象的实例方法,如 obj::equals 代表调用 obj 的 equals 方法与接口方法参数比较是否相等,效果等同 obj.equals(t);。

  • 1.2.3ClassName :: instanceMethod
    较为特殊,将接口方法参数列表的第一个参数作为方法调用者,其余参数作为方法参数。由于此类接口较少,故选择 Java 提供的 BiFunction 接口作为示例,该接口方法接收一个 T1 类对象和一个 T2 类对象,通过处理后返回 R 类对象:

    image.png

1.3构造器引用

Lambda 表达式的第三种形式,其实和方法引用十分相似,只不过方法名替换为 new 。其格式为 ClassName :: new。这时编译器会通过上下文判断传入的参数的类型、顺序、数量等,来调用适合的构造器,返回对象。
我们来定义一个实体类UserBean

public class UserBean {


    String userName;
    int age;

    public UserBean(String userName) {
        this.userName = userName;
    }

    public UserBean(){
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public UserBean(String userName, int age) {
        this.userName = userName;
        this.age = age;
    }


    @Override
    public String toString() {
        return "UserBean{" +
                "userName='" + userName + '\'' +
                ", age=" + age +
                '}';
    }
}

接下来定义一个返回UserBean的接口:

public interface Construction<T> {
    T getBean(String name ,Integer age);
}

具体使用:

/**
     * 通过Lamdba构造器获取对象
     */
    void constructorMthod() {
        Construction<UserBean> construction = UserBean::new;
        UserBean userBean =construction.getBean("张三",15);
        System.out.println(userBean.toString());
    }

2.其他说明

2.1 变量捕获

在使用匿名内部类时,若要在内部类中使用外部变量,则需要将此变量定义为 final 变量。因为我们并不知道所实现的接口方法何时会被调用,所以通过设立 final 来确保安全。在 lambda 表达式中,仍然需要遵守这个标准。

不过在 Java 8 中,新增了一个 effective final 功能,只要一个变量没有被修改过引用(基本变量则不能更改变量值),即为实质上的 final 变量,那么不用再在声明变量时加上 final 修饰符,也就是说我们可以不做任何声明上的改变即可在 lambda 中使用外部变量,前提是我们以 final 的规则对待这个变量。

2.2 this 关键字

在匿名内部类中,this 关键字指向的是匿名类本身的对象,而在 lambda 中,this 指向的是 lambda 表达式的外部类。

2.3 方法数量差异

当前 Android Studio 对 Java 8 新特性编译时采用脱糖(desugar)处理,lambda 表达式经过编译器编译后,每一个 lambda 表达式都会增加 1~2 个方法数。而 Android 应用的方法数不能超过 65536 个。虽然一般应用较难触发,但仍需注意。

2.4 默认方法

在Java语言中,一个接口中定义的方法必须由实现类提供实现。但是当接口中加入新的API时, 实现类按照约定也要修改实现,而Java8的API对现有接口也添加了很多方法,比如List接口中添加了sort方法。 如果按照之前的做法,那么所有的实现类都要实现sort方法,JDK的编写者们一定非常抓狂,那应当怎么办呢?
在Java8种引入新的机制,支持在接口中声明方法同时提供实现。 这令人激动不已,你有两种方式完成 1.在接口内声明静态方法 2.指定一个默认方法。
如我们上面定义的Predicate接口:

image.png

调用执行 :

     Predicate predicate = o -> false;
        predicate.testMethod();

基本就到这里了,拜拜。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,240评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,328评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,182评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,121评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,135评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,093评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,013评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,854评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,295评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,513评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,678评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,398评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,989评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,636评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,801评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,657评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,558评论 2 352

推荐阅读更多精彩内容