多应用+插件架构,代码干净,二开方便,首家独创一键云编译技术,文档视频完善,免费商用码云13.8K 广告
[TOC] # Lambda表达式 lambda 表达式是Java8版本的新特性,采用一种简洁的语法定义代码块。Android Studio 3.0 及更高版本支持所有 Java 7 语言功能,以及部分 Java 8 语言功能(具体因平台版本而异),需要为每个模块单独配置使用Java8语言的新特性。 1、图形化配置 ![](https://img.kancloud.cn/50/9f/509f4a2a93bfa0921468bb59e17e4c26_1400x913.png) 2、代码配置 直接在相应模块的build.gradle文件中进行配置: ```plain android { ... compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } // Kotlin 项目按如下配置 kotlinOptions { jvmTarget = "1.8" } } ``` ## 定义 在Android开发中,经常需要为一个控件设置点击事件监听器,如下: ```java mTvLogin.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Toast.makeText(mContext, "click ", Toast.LENGTH_SHORT).show(); } }); ``` 上面的例子中,将一个代码块(onClick方法)传递给了一个对象(mTvLogin),并且这个代码块会在将来某个时间调用(点击时调用)。 但是在Java中,并不能直接传递一个代码段,因为Java是一种面向对象语言,所以必须构造一个对象,这个对象的类需要有一个方法能包含所需的代码。在上面代码中,实际上是构造了一个类实现了OnClickListener接口,并且new了一个对象传递给了mTvLogin的setOnClickListener方法,由于只需要使用一次,因此使用了匿名内部类的形式来创建对象。 上述代码的lambda表达式写法如下: ```java mTvLogin.setOnClickListener((View v) -> Toast.makeText(mContext, "click ", Toast.LENGTH_SHORT).show()); ``` 格式为:参数,箭头(->)以及一个表达式。关于lambda表达式的格式需要注意以下几点: * 如果代码块需要完成的计算无法放在一个表达式中,可以把代码放在{}中,并可以包含return语句。 * 如果可以推导出一个lambda表达式的参数类型,写的时候可以忽略其类型。`(v) -> Toast.makeText(mContext, "click ", Toast.LENGTH_SHORT).show()` * 如果方法只有一个参数,且这个参数的类型可以推导得出,甚至可以省略小括号。`v -> Toast.makeText(mContext, "click ", Toast.LENGTH_SHORT).show()` * 即使lambda表达式没有参数,仍然要提供空括号,就像无参数的方法一样。`() -> Toast.makeText(mContext, "click ", Toast.LENGTH_SHORT).show()` * 无需指定lambda表达式的返回类型,会根据上下文推导得出。`(String first, String second) -> first.length() - second.length()`可以在需要int类型结果的上下文中使用,不需要显式书写return语句 ## 函数式接口 对于只有一个抽象方法的接口,需要这种接口的对象时,可以不再使用匿名内部类的形式,而是提供一个lambda表达式。这种接口称为函数式接口(functional interface)。 ## 方法引用 当已经有现成的方法可以完成你想要传递到其他代码的某个动作时, ```java mTvLogin.setOnClickListener(v -> System.out.println(v)); ``` 可以按如下调用: ```java mTvLogin.setOnClickListener(System.out::println); ``` 表达式`System.out::println`是一个方法引用,等价于lambda表达式`v -> System.out.println(v)`。`::`操作符用于分隔方法名与对象或类名,有以下3种情况: 1、`object::instanceMethod`等价于`x -> object.instanceMethod(x)` 2、`Class:staticMethod`等价于`Class.staticMethod(x)` 3、`Class:instaceMethod`等价于`(x, y) -> x.instaceMethod(y)` 第三种情况比较特殊,第一个参数会成为方法的目标,例如,`String::compareToIgnoreCase`等同于`(x, y) -> x.compareToIgnoreCase(y)` 方法引用中可以使用this参数,`this::equals`等同于`x -> this.equals(x)`;同样使用super参数也是合法的,`super::equals`等同于`x -> super.equals(x)`,会调用父类的equals方法。 ## 变量作用域 原来使用匿名内部类时,编译后局部内部类会有一个成员变量,是对外部局部变量的引用的拷贝,使用外部局部变量时都是通过这个引用进行的。为避免这个成员变量的值被外部类的方法修改,导致使用时得到不一样的值,需要使用final字段让该变量不可变。 由于lambda表达式以及方法引用,都不能单独存在,总是会转换为函数式接口的实例。所以这一点同样适用于lambda表达式,lambda表达式可以捕获外部作用域中的变量的值。