关于Java8新特性的一个问题
前几天在一个直播课程上看到这样一种写法:
public class AtomicDemo {
public static void hellooo() {
System.out.println("hello");
}
public static void main(String[] args){
new Thread(AtomicDemo::hellooo).start();
}
}
运行结果:输出 hello
看到这个例子,我不太明白new Thread(AtomicDemo::hellooo)这种写法是怎么回事,Thread的构造方法里不是应该传入Runnable对象吗?
我知道这是Java 8的新特性,可是具体是怎么回事就不太清楚了,于是自行求助网络,现在将自己的一些理解记下来:
lambda表达式实例
首先,Java 8有个新特性,叫lambda表达式,这个大家应该都听过。
什么是lambda表达式呢,网上的讲解很多,我这里就不细讲了,基本语法可以这么表示:
(参数) -> 含参数的表达式
或
(参数) ->{ 对参数进行处理的代码块 }
这个参数如果是多个,要用逗号隔开。如果是一个参数,可以省略参数的左右括号。如果没有参数,括号不能省略。
lambda表达式可以用它来简化代码,比如在Android项目里,一般我们给Button设置点击事件,可以这么写:
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
btn.setText("clicked");
}
});
但是如果换成lambda表达式,可以简化为这样:
btn.setOnClickListener((View v) -> v.setText("clicked"));
再简化下,可以这样:
btn.setOnClickListener( v -> v.setText("clicked"));
很简洁吧,这样写,避免了写匿名内部类的繁琐。不过为什么可以这么写呢?因为Java8还有个新特性,叫函数式接口。
函数式接口的简介
简单说,就是只含有一个抽象方法的接口,比如 View.OnClickListener接口就是函数式接口,它只有一个onClick()抽象方法。
注意函数式接口只有一个抽象方法,但是它可以有静态方法(即static修饰的方法)和默认方法(用default修饰的方法),后两种方法就算有具体实现也可以的。
在Java8中,函数式接口比较常见的如Runnable接口,Callable接口,Comparator接口等,在源码中,它们已经被加上了@FunctionalInterface标签:
@FunctionalInterface
public interface Runnable {
public abstract void run();
}
我们在写函数式接口时,不加这个标签也可以,但加上有助于更好地让编译器做检查,如果不符合函数式接口的规范,编译器会报错。
而且,Java8的设计者为了更好地支持lambda表达式,于是让函数式接口可以隐式地转化为lambda表达式。所以,文章开头那个例子的main()方法可以这么写:
Runnable runnable = () -> {System.out.println("hello");};
new Thread(runnable).start();
或者省略大括号:
Runnable runnable = () -> System.out.println("hello");
new Thread(runnable).start();
或者直接用定义好的方法;
Runnable runnable = () -> hellooo();
new Thread(runnable).start();
这三种写法都一样的。
实际上我随便写一个函数式接口,比如叫做MyInterface,那么我就可以MyInterface face = () -> hellooo()这么写都是可以的,只是这里的face对象就不能传入Thread构造方法里了。
更直接一点,可以这么写:
new Thread(() -> hellooo()).start();
也可以。但是和我们开头例子中的new Thread(AtomicDemo::hellooo).start()还是不一样,这种双冒号的写法是怎么回事呢?这就是Java 8的另一个新特性:方法引用。
方法引用
介绍方法引用之前,先看几个方法引用的简单例子:
如果我们构造了一个Arraylist,
List<String> colorList = Arrays.asList("red", "yellow", "blue");
然后想遍历打印,如果用方法引用,只需要一行代码:
colorList.forEach(System.out::println); //forEach也是Java8的新特性
方法引用简单来说,就是类名/对象+双冒号+方法名的写法,具体有下列这么多,此处就不细讲了。它和lambda表达式都是Java的语法糖。
静态方法引用:ClassName::methodName
实例上的实例方法引用:instanceReference::methodName
超类上的实例方法引用:super::methodName
类型上的实例方法引用:ClassName::methodName
构造方法引用:Class::new
数组构造方法引用:TypeName[]::new
那么开头例子中,我们用静态方法引用的写法,就可以写成这样:
Runnable runnable = AtomicDemo::hellooo;
new Thread(runnable).start();
简化一下,就成了开头的写法new Thread(AtomicDemo::hellooo).start()。
如何让Android项目支持Java 8的这些新特性
如果IDE用的是Android studio,可以这么做:
1.在根目录下的build.gradle添加:
dependencies {
...
classpath 'me.tatarka:gradle-retrolambda:3.2.5' //添加这行
...
}
2.在app下的build.gradle添加:
apply plugin: 'me.tatarka.retrolambda'
3.在app下的build.gradle中指定Java版本为java8:
android {
...
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
...
}
这样就可以在Android项目中使用Java8新特性了。