标签:创建 顺序 rgs 简单 system 接口 编译器 interface 推断
一文看懂 Java8 的 Lambda表达式!IT领域的技术日新月异,Java14很快就要问世了,但是对于国内的许多程序员来说,连Java8都还没有真正掌握。
今天,我们就来温习一下Java8的特性之一,Lambda表达式。
以往,使用单一抽象方法的接口被用作函数类型。它们的实例表示函数(functions)或行动(actions)。自从 JDK 1.1 于 1997 年发布以来,创建函数对象的主要手段就是匿名类。
匿名类,通俗地讲,就是没有类名,直接通过new关键字创建这个类的实例。下面是匿名类的一个例子:
java.util包中的Comparator接口
public interface Comparator<T> {
int compare(T o1, T o2);
}
使用匿名类创建排序的比较方法(强制排序顺序):
Collections.sort(words, new Comparator<String>() {
public int compare(String s1, String s2) {
return Integer.compare(s1.length(), s2.length());
}
});
匿名类适用于需要函数对象的经典面向对象设计模式,特别是策略模式,上面的匿名类是排序字符串的具体策略。然而,匿名类确实过于冗长。
在 Java 8 中,语言形式化了这样的概念,即使用单个抽象方法的接口是特别的,应该得到特别的对待。这些接口现在称为函数式接口,并且该语言允许你使用lambda 表达式或简称 lambda 来创建这些接口的实例。Lambdas 在功能上与匿名类相似,但更为简洁。下面的代码使用 lambdas 替换上面的匿名类。清晰明了
Collections.sort(words,(s1, s2) ->
Integer.compare(s1.length(), s2.length()));
那我们再来一个简单的例子:
首先我们定义一个Learn接口
@FunctionalInterface
public interface Learn{
void study();
}
为了获取 Learn接口的实现对象,可以为该接口定义一个实现类 StudyDemo
public class StudyDemo implements Learn{
@Override
public void study() {
System.out.println("好好学习,天天向上");
}
}
然后创建该实现类的对象调用study方法:
Learn s = new StudyDemo ();
s.study();//运行结果-->好好学习,天天向上
如果StudyDemo 类只是为了实现 Learn接口而存在的,且仅被使用了一次,所以使用匿名内部类来简化这一操作:
public static void main(String[] args) {
studyInterface s = new studyInterface () {
@Override
public void study() {
System.out.println("好好学习,天天向上");
}
};
s.study();//运行结果-->好好学习,天天向上
}
显然,使用匿名内部类还不够简洁,所以我们用lambda表达式来优化:
因为Learn接口中只有一个方法,编译器使用称为类型推断的过程从上下文中推导出这些类型和方法,所以我们可以省去study的方法名和new Learn,并加上箭头 ->
public static void main(String[] args) {
Learn s = ()->{
System.out.println("好好学习,天天向上");
}
};
s.study();//运行结果-->好好学习,天天向上
}
如果像上面一样lambda表达式只有一行代码,我们可以进一步优化:
public static void main(String[] args) {
Learn s = ()-> System.out.println("好好学习,天天向上");
s.study(); //运行结果-->好好学习,天天向上
}
一行代码对于 lambda 说是理想的,三行代码是合理的最大值。如果违反这一规定,可能会严重损害程序的可读性。如果一个 lambda 很长或很难阅读,要么找到一种方法来简化它或重构你的程序来消除它。
上面我们举的例子中,study()方法既没有参数也没有返回值,如果有参数和返回值又该怎么办呢?
首先定义一个接口中的唯一抽象方法带参数的情况:
interface Learn1{
void study(int a,int b);
}
再看看它的匿名内部类和lambda表达式写法:
public static void main(String[] args) {
//先定义一个接口的引用
Learn1 learn;
//匿名内部类写法
learn = new Learn1() {
@Override
public void study(int a,int b) {
System.out.println("好好学习x"(a+b));
}
};
learn.study(1,2);//运行结果-->好好学习x2
//lambda表达式写法,省略接口和方法名,
//方法的参数类型可以推导出来,也可以省略
learn = (a,b)->{
System.out.println("好好学习x"(a+b));
};
learn.study(3,4);//运行结果-->好好学习x12
如果方法中只有一个参数,还可以省略小括号,Lambda表达式中只有一条语句可以省略大括号
learn = e-> System.out.println("好好学习"+e);
learn.study(5);
再来看看有返回值的情况,再回过头来看java.util包中的Comparator接口:
public interface Comparator<T> {
int compare(T o1, T o2);
}
先定义一个学生类:
public class Student {
private String name;
private int age;
public Student(String ,int age){
this.name = name;
this.age = age;
}
public int getAge(){
return this.age;
}
接下来我们对数组中的Student对象,使用Arrays的sort方法通过年龄进行升序排序
public static void main(String[] args) {
Student[] array = {
new Student("张三", 18),
new Student("李四", 20),
new Student("王五", 19) };
//匿名内部类写法
Comparator<Student > compare = new Comparator<Student >() {
@Override
public int compare(Student s1, Student s2) {
return s1.getAge() ‐ s2.getAge();
}
};
//调用
Arrays.sort(array, compare);
//lambda表达式写法,去掉接口名和方法名
Comparator<Student > compare = (Student s1, Student s2)->{
return s1.getAge() ‐ s2.getAge();
}
};
//调用
Arrays.sort(array, compare);
//省略写法,去掉参数类型,只保留返回值
Comparator<Student > compare = (s1,s2)-> s1.getAge() ‐ s2.getAge();
说明:
在Lambda表达式标准形式的基础上:
综上所述,从 Java 8 开始,lambda 是迄今为止表示小函数对象的最佳方式。除非必须创建非函数式接口类型的实例,否则不要使用匿名类作为函数对象。
备注:有且仅有一个抽象方法的接口,称为“函数式接口”。
参考书目:《Effective Java》
感谢 LA同学 的投稿
—————END—————
喜欢本文的朋友,欢迎关注公众号 程序员小灰,收看更多精彩内容
欢迎长按二维码关注 小灰学英语,你所学到的不只是英语!
给个[在看],是对小灰最大的支持!
标签:创建 顺序 rgs 简单 system 接口 编译器 interface 推断
原文地址:https://blog.51cto.com/14982143/2549617