目标:了解使用匿名内部类存在的问题,体验Lambda 匿名内部类存在的问题:当需要启动一个线程去完成任务时,通常会通过Runnable 接口来定义任务内容,并使用Thread 类来启动该线程。
public class LambdaIntro01 {
public static void main(String[] args) {
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("启动一个线程");
}
}).start();
}
}
代码分析:
由于面向对象的语法要求,首先创建一个Runnable 接口的匿名内部类对象来指定线程要执行的任务内容,再将其交给一个线程来启动。 对于Runnable 的匿名内部类用法,可以分析出几点内容:
Lambda是一个匿名函数,可以理解为一段可以传递的代码。
public class LambdaIntro01 {
public static void main(String[] args) {
new Thread(() ->{
System.out.println("启动一个线程");
}).start();
}
}
代码分析:
这段代码和刚才的执行效果是完全一样的,可以在JDK 8或更高的编译级别下通过。从代码的语义中可以看出:我们启动了一个线程,而线程任务的内容以一种更加简洁的形式被指定。我们只需要将要执行的代码放到一个Lambda表达式中,不需要定义类,不需要创建对象。
Lambda省去面向对象的条条框框,Lambda的标准格式格式由3个部分组成:
(参数类型 参数名称) -> {
代码体;
}
格式说明:
(参数类型 参数名称):参数列表 {代码体;}:方法体 -> :箭头,分隔参数列表和方法体
public interface PhoneStore {
public abstract void buy();
}
public class LambdaUse02 {
public static void main(String[] args) {
goStore(new PhoneStore() {
@Override
public void buy() {
System.out.println("买华为手机");
}
});
goStore(() -> System.out.println("买小米手机"));
}
public static void goStore(PhoneStore phoneStore){
phoneStore.buy();
}
}
下面举例演示java.util.Comparator<T> 接口的使用场景代码,其中的抽象方法定义为:
public abstract int compare(T o1, T o2);
当需要对一个对象集合进行排序时, Collections.sort 方法需要一个Comparator 接口实例来指定排序的规则。
实体类
public class Person {
private String name;
private Integer age;
private Date birthday;
public Person(String name, Integer age, Date birthday) {
this.name = name;
this.age = age;
this.birthday = birthday;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
@Override
public String toString() {
return new StringJoiner(", ", Person.class.getSimpleName() + "[", "]")
.add("name='" + name + "'")
.add("age=" + age)
.add("birthday=" + birthday)
.toString();
}
}
传统写法VS Lambda写法
传统写法
public class Lambda03 {
public static void main(String[] args) {
ArrayList<Person> persons = new ArrayList<>();
persons.add(new Person("刘德华", 58, new Date()));
persons.add(new Person("张学友", 58, new Date()));
persons.add(new Person("刘德华", 54, new Date()));
persons.add(new Person("黎明", 53, new Date()));
Collections.sort(persons, new Comparator<Person>() {
@Override
public int compare(Person o1, Person o2) {
return o1.getAge() - o2.getAge();
}
});
for (Person person : persons) {
System.out.println(person);
}
}
}
Lambda写法
public class Lambda03 {
public static void main(String[] args) {
ArrayList<Person> persons = new ArrayList<>();
persons.add(new Person("刘德华", 58, new Date()));
persons.add(new Person("张学友", 58, new Date()));
persons.add(new Person("刘德华", 54, new Date()));
persons.add(new Person("黎明", 53, new Date()));
Collections.sort(persons,(o1, o2)->{
return o1.getAge() - o2.getAge();
});
//另一种写法
Collections.sort(persons, Comparator.comparingInt(Person::getAge));
for (Person person : persons) {
System.out.println(person);
}
}
}
在Lambda标准格式的基础上,使用省略写法的规则为:
(int a) ->{
return new Person();
}
省略后
a -> new Person();
Lambda的语法非常简洁,但是Lambda表达式不是随便使用的,使用时有几个条件要特别注意:
public interface Flyable {
public abstract void flying();
}
public class Lambda04 {
public static void main(String[] args) {
test(() ->{});
Flyable flyable = new Flyable() {
@Override
public void flying() {
}
};
Flyable flyable1 = () ->{
};
}
public static void test(Flyable flyable){
flyable.flying();
}
}
函数式接口在Java中是指:有且仅有一个抽象方法的接口。 函数式接口,即适用于函数式编程场景的接口。而Java中的函数式编程体现就是Lambda,所以函数式接口就是可以适用于Lambda使用的接口。只有确保接口中有且仅有一个抽象方法,Java中的Lambda才能顺利地进行推导。
FunctionalInterface注解
与@Override 注解的作用类似,Java 8中专门为函数式接口引入了一个新的注解:@FunctionalInterface 。该注解可用于一个接口的定义上:
@FunctionalInterface
public interface Operator {
void myMethod();
}
一旦使用该注解来定义接口,编译器将会强制检查该接口是否确实有且仅有一个抽象方法,否则将会报错。不过,即使不使用该注解,只要满足函数式接口的定义,这仍然是一个函数式接口,使用起来都一样。
不同 | Lambda | 匿名内部类 |
---|---|---|
所需的类型 | 需要的类型必须是接口 | 需要的类型可以是类,抽象类,接口 |
抽象方法的数量 | 所需的接口只能有一个抽象方法 | 所需的接口中抽象方法的数量随意 |
实现原理 | 是在程序运行的时候动态生成class | 是在编译后会形成class |
总结:当接口中只有一个抽象方法时,建议使用Lambda表达式,其他其他情况还是需要使用匿名内部