专栏首页Android-薛之涛Android-Lambda表达式

Android-Lambda表达式

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即可,如图:

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

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

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

我去,,第一眼感觉就是代码变少了。这其实也是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 对象的姓名字符串并返回:

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包:

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

  • 1.2.2 object :: instanceMethod 直接调用任意对象的实例方法,如 obj::equals 代表调用 obj 的 equals 方法与接口方法参数比较是否相等,效果等同 obj.equals(t);。
  • 1.2.3ClassName :: instanceMethod 较为特殊,将接口方法参数列表的第一个参数作为方法调用者,其余参数作为方法参数。由于此类接口较少,故选择 Java 提供的 BiFunction 接口作为示例,该接口方法接收一个 T1 类对象和一个 T2 类对象,通过处理后返回 R 类对象:

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接口:

调用执行 :

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

基本就到这里了

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Java-策略设计模式

    今天 我们来学习一下策略模式,什么是策略模式呢?比如我们一个功能的实现可以有多个策略去选择,比如:出行方式可以选择:共享单车,拼车,私家车,出租等。如果将这个出...

    android_薛之涛
  • Android - 懒加载

    如果我们的项目中使用了ViewPager+Framgment实现底部Tab可点可滑,那么我们都知道ViewPager有预加载功能,通过viewpager.set...

    android_薛之涛
  • Android-Retrofit简介

    上一篇文章讲了RxJava,这一篇当然就该讲Retrofit了,参考资料: https://blog.csdn.net/gumufuyun/article/d...

    android_薛之涛
  • Servlet的源码分析

    4 .使用 ctrl+o 即可查看该类的所有属性与方法 注 : 每种图标代表不同的方法 ,像是第一个红色方框加SF 代表 private 类型的最终静态常量...

    时间静止不是简史
  • 接口测试其实很简单

    在软件行业中,软件测试算是入行门槛比较低的工作了,相信有很多小伙伴是在犹豫要不要转行做测试,或者又担心没有测试基础怎么办,作为测试,我认为必会的就是接口测试了,...

    小雯子打豆豆
  • javax顶层接口分析

    代码改变世界-coding
  • Java-Lambda表达式和“方法引用”的对比和详解

     Lambda表达式是Java 8 添加的一个新特性,可以认为,Lambda是一个匿名函数(相似于匿名内部类),作用是返回一个实现了接口的对象(这个观点非常重要...

    Fisherman渔夫
  • Java 学习笔记(11)——lambda 表达式

    在写Java代码的时候,如果某个地方需要一个接口的实现类,一般的做法是新定义一个实现类,并重写接口中的方法,在需要使用的时候new一个实现类对象使用,为了一个简...

    Masimaro
  • JDK8新特性总结

    Lambda表达式是一个新的语言特性,已经在JDK8中加入。它是一个可以传递的代码块,你也可以把它们当做方法参数。Lambda表达式允许您更紧凑地创建单虚方法接...

    掌上编程
  • python接口测试面试题

    就算所有人都不支持你。这条路会很曲折,你也会一度认为是不是自己选错了,但只要坚持,就算最后没有成功,但努力了就不会有遗憾。

    测试小兵

扫码关注云+社区

领取腾讯云代金券