前言
其实Java8已经出现了很久,听闻Java9都准备出了,但是现在普及率并不不是很高,主流的开发环境依然是Java7,甚至有些依然在使用Java5等比较老旧的开发环境。作为一名Android开发者,Java是我们的主要开发语言,我们还是很有必要去了解并且使用最新型的技术,而且Java8中有许多新的特性也是有利于提高我们开发效率的,下面我来一一介绍一下。
本文参考了很多网络上已有的文章,且已注明出处。
介绍
在Android编程中使用Java8新特性,需要先在Android studio中配置一下,具体配置方法,可参考此配置。
接口默认方法
在java8中,接口可以添加默认的方法以及静态方法,其类型也是public abstract的类型,实现方式如下:
public interface DataSource<T> {
List<T> reqeustData(String name);
@RequiresApi(api = Build.VERSION_CODES.N)
default void defaultMethod() {
Log.i("DataSource", "i am defaultMethod");
}
static void staticMethod(){
Log.i("DataSource", "i am staticMethod");
}
}
需要注意的是,接口默认方法现在只能在Android N以上才支持,所以使用的时候要添加声明。
相比于传统的接口,我们在Java8中可以添加默认的方法,而这个默认方法的类型同样是public 的,所以我们可以把它当做正常方式使用,只是无需去实现。在实际项目当中,当一个接口被多个类实现时,我们去拓展接口里面的方法时会导致工程量变大,而当我们引入接口默认方法时,就无需去修改每个类。
当一个接口扩展另外一个包含默认方法的接口的时候,有以下3种处理方式。
- 完全无视默认方法
直接继承了上级接口的默认方法 - 重新申明默认方法
重新把默认方法申明为抽象方法(无实现,具体子类必需再次实现该方法) - 重新实现默认方法
重写了默认方法的实现,依然是一个默认方法。
我觉得如果对访问权限没有特别要求的,接口基本上能够代替抽象类,而且还能实现多继承。如果对抽象类和接口默认方法感兴趣的童鞋,可以参考这篇文章Java 8 之默认方法(Default Methods)。
Lambda表达式
Lambda表达式又称为闭包或匿名方法,我觉得lambda表示式式Java8中最受欢迎的一个特性,因为只要用习惯了这个表达式,就不会再想去用旧的方法来实现了。
Lambda表达式我相信很多人已经听说过,所以我就不做过多介绍,主要说一下我在Android开发中使用到的一些场景:
button.setOnClickListener(v->showLog("hello"));
new Thread(()->showLog("hello")).start();
//多个参数的写法
List<String> names = Arrays.asList("nickming", "peter", "mike");
Collections.sort(names, (a, b) -> b.compareTo(a));
names.forEach(this::showLog);
当然,这只是lambda运用的一部分示例,总结起来lambda的作用就是:
- 简化代码,可以不用再写匿名内部类,可以将多行代码简化为一行代码。
- 提供函数式接口,实现函数式编程
- 提升Java性能
强烈建议在编程中多使用lambda表达式,特别是配合RxJava以及RxAndroid的使用,写起代码真的是行云流水一气呵成。感兴趣的童鞋可以参考这篇文章Lambda表达式的意义。
但是使用lambda表达式需要注意几点:
- 在lambda表达式中可以直接访问外层final变量、实例、静态变量。
- 访问未标记final的变量时,该变量会具有隐形final属性,如果后续更改变量值,会导致编译不通过。
- 在lambda表达式中,对于内部实例和静态变量可读可写。
- lambda无法访问到接口的默认方法。
函数式接口
“函数式接口”是指仅仅只包含一个抽象方法的接口,每一个该类型的lambda表达式都会被匹配到这个抽象方法。因为 默认方法 不算抽象方法,所以你也可以给你的函数式接口添加默认方法。
@FunctionalInterface
public interface Converter<F,T> {
T convert(F from);
}
Converter<Integer,String> converter= (f)->String.valueOf(f);
converter.convert(1);
Converter<String,Integer> converter3=Integer::new;
Converter<String,Integer> converter2=Integer::valueOf;
这是我们自定义的函数式接口,其实在Java8中已经给很多接口都预置了FunctionalInterface这个关键字,所以这是Java8支持函数式编程的一个重要原因之一,像Runnable、Comparator等接口在Java8中都是标记有FunctionalInterface关键字的。感兴趣的童鞋可以参考这篇文章函数式接口。
由于是函数式编程,在我个人理解就是将函数当做编程的基础,区别于我们之前的面向对象的编程,我们所有操作都以函数为基础,函数可以当做其他函数的变量参数,也可以当做其他函数的返回结果。
既然Java8支持函数式编程,所以其提供了方法与构造函数引用这个方式,就像我们可以直接通过Integer::new来实现converter,new是直接实现integer的构造函数,valueOf是其方法引用,只要其返回结果是接口需要实现的返回类型即可。
其实在许多场景下,我们可以直接利用 :: 这个符号来实现方法的引用,例如上文提到的
names.forEach(this::showLog)
这一行代码中,我们省略了for循环,实际上forEach函数式stream特性的一个,下文会提到。forEach函数需要实现Consumer接口,接着在实现其accept方法:
names.forEach(new Consumer<String>() {
@Override
public void accept(String s) {
showLog(s);
}
});
//lambda表达式
names.forEach(s->showLog(s));
//方法引用
names.forEach(this::showLog);
直接组合利用lambda表达式和方法引用,可以简化代码,表达值简单明了。
常见的函数接口
Java8实际上提供了很多常用的函数式接口,都位于package java.util.function包下面,常用的如下:
- Predicate 接口只有一个参数,返回boolean类型。该接口包含多种默认方法来将Predicate组合成其他复杂的逻辑(比如:与,或,非)
- **Function **接口有一个参数并且返回一个结果,并附带了一些可以和其他函数组合的默认方法(compose, andThen)
- **Supplier **接口返回一个任意范型的值,和Function接口不同的是该接口没有任何参数
- **Consumer **接口表示执行在单个参数上的操作
- **Comparator **是老Java中的经典接口, Java 8在此之上添加了多种默认方法
- **Optional **被定义为一个简单的容器,其值可能是null或者不是null。在Java 8之前一般某个函数应该返回非空对象但是偶尔却可能返回了null,而在Java 8中,不推荐你返回null而是返回Optional
我就不仔细分析每个接口的作用了,主要参考函数式接口这篇文章。
其实,当你尝试过使用RxJava和RxAndroid后,你会发现这些x里面的很多概念都是做法都是和Java8中很相似的,都是提供了各种函数操作符,各种函数接口实现。
小结
在这篇文章中,主要是介绍了一下接口默认方法、lambda表达式、函数式接口、方法构造函数的引用这几个特性,在下一篇文章中会着重介绍一下stream、Date API、Annotation、CompletableFuture几个特性。