1.lambda表达式(函数式编程)
当需要启动一个线程去完成任务时,通常会通过java.lang.Runnable接口来定义任务内容,并使用java.lang.Thread类来启动该线程。
代码如下:
-
方法一
//自定义一个线程类Thread2
public class TreadT2 implements Runnable {
@Override
public void run() {
for(int i=0;i<10;i++){
//打印当前进程信息
System.out.println(Thread.currentThread().getName()+
" "+i);
}
}
public static void main(String[] args) {
TreadT2 t2 = new TreadT2();
Thread thread = new Thread(t2,“小强”);
Thread thread1 = new Thread(t2,“小李”);
thread.start();
thread1.start();
} -
方法二
public static void main(String[] arg){
Runnable runnable = new Runnable() { @Override public void run() { System.out.println(Thread.currentThread().getName() + ""); } }; Thread t=new Thread(runnable); t.start(); //start()开启线程 }
对于方法二可以用lambda函数简化代码
面向对象的思想:
做一件事,找一个能解决这个事情的对象,调用对象的方法,完成事情
函数式编程的思想:
只要能获得结果,谁去做的,怎么做都不重要,重视的是结果,不重视过程
代码分析
注意:java中Thread类实现Object类并实现Runnable接口
对于Runnable的匿名内部类用法,可以分析出:
-
Thread类需要Runnable接口作为参数,其中的抽象run方法是用来指定线程任务内容的核心;
-
为了省去定义一个RunnableImpl实现类的麻烦,不得不使用匿名内部类;
-
必须覆盖重写抽象run方法、所有方法名称、方法参数、方法返回值不得再重写一遍;
-
借助java8的全新语法,匿名内部类写法更简单
public static void main(String[] arg){new Thread(()->System.out.println(Thread.current.getName()+" ")).satrt(); //启动线程 } /* *对比一下 */ Runnable runnable = new Runnable() { @Override public void run() { System.out.println(Thread.currentThread().getName() + ""); } }; Thread t=new Thread(runnable); t.start(); //start()开启线程
这段代码和刚才的执行效果是完全一样的,可以在1.8或更高的编译级别下通过。从代码的语义中可以看出:我们启动了一个线程,而线程任务的内容以一种更加简洁的形式被指定。
不再有“不得不创建接口对象”的束缚,不再有“抽象方法覆盖重写”的负担,就是这么简单!
匿名内部类的好处与弊端
一方面,匿名内部类可以帮我们省去实现类的定义;另一方面,匿名内部类的语法——确实太复杂了!
语义分析
仔细分析该代码中的语义,Runnable接口只有一个run方法的定义:public abstract void run()
;
即制定了一种做事情的方案(其实就是一个函数):- 无参数:不需要任何条件即可执行该方案。
- 无返回值:该方案不产生任何结果。
- 代码块(方法体):该方案的具体执行步骤。
同样的语义体现在Lambda语法中,要更加简单:
() -> System.out.println("**********")
- 前面的一对小括号即run方法的参数(无),代表不需要任何条件;
- 中间的一个箭头代表将前面的参数传递给后面的代码;
- 后面的输出语句即业务逻辑代码。
如同Runnable接口还有Comparator(compar抽象方法)和FileFilter(accept抽象方法),
2.Lambda的参数和有返回值
2.1 Comparator接口
当需要对一个对象数组进行排序时,Arrays.sort方法需要一个java.util.Comparator接口实例来指定排序的规则。假设有一个Person类,含有age、name属性:
public class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
}
使用Comparator接口的写法
import java.util.Arrays;
import java.util.Comparator;
public class MenP {
public static void main(String[] args) {
// 本来年龄乱序的对象数组
Person[] array = {
new Person("古力娜扎", 19),
new Person("迪丽热巴", 18),
new Person("马尔扎哈", 20) };
// 匿名内部类
Comparator<Person> comp = new Comparator<Person>() {
@Override
public int compare(Person o1, Person o2) {
return o1.getAge() - o2.getAge();
}
};
Arrays.sort(array, comp);
// 第二个参数为排序规则,即Comparator接口实例
for (Person person : array) {
System.out.println(person);
}
}
}
代码分析
- 为了排序,Array.sort方法需要排序规则,即Comparator接口的实例抽象方法compare是关键
- 为了省去定义一个ComparatorImpl实现类的麻烦,不得不使用内名内部类
- 必须重写抽象compare方法,所以方法名称、方法参数、方法返回值需要重写一次,不能写错
Lambda写法
import java.util.Arrays;
import java.util.Comparator;
public class MenP {
public static void main(String[] args) {
// 本来年龄乱序的对象数组
Person[] array = {
new Person("古力娜扎", 19),
new Person("迪丽热巴", 18),
new Person("马尔扎哈", 20) };
// 匿名内部类(lambda)
Arrays.sort(array, (a,b)->{
return a.getAge()-b.getAge();
});
// 第二个参数为排序规则,即Comparator接口实例
for (Person person : array) {
System.out.println(person);
}
}
}
2.2 FileFilter
java.io.FilteFilter是个接口,时File的过滤器。该接口的对象可以传递给File类的listFiles(FileFilter)作为参数,接口中只有一个方法boolean accept(File pathname)测试pathname是否应该包含在当前File目录中,符合则返回true。
代码分析
- 接口作为参数,需要传递子类对象,重写其中方法
- accept方法,参数为File,表示当前File下所有的子文件和子目录。保留住了返回true,过滤掉返回false。规则:
1、要么是Java文件。
2、要么是目录,用于继续遍历
代码实现
import java.io.File;
import java.io.FileFilter;
public class fileFilter {
public static void main(String[] args) {
File file = new File("E:\\文件\\idea2018\\src");
print(file);//调用自定义方法
}
//自定义方法
public static void print(File file){
File[] files = file.listFiles(new FileFilter() {
@Override
public boolean accept(File pathname) {
//判断是Java文件或是目录,是则保存并返回true
return pathname.getName().endsWith(".java")||pathname.isDirectory();
}
});
//判断如果是文件打印文件路径,否则继续调用print方法
for (File f:files){
if (f.isFile()){
System.out.println("文件名"+f.getAbsolutePath());
}else{
print(f);
}
}
}
}
Lambda写法
public static void print(File file){
/**
*
* lambda形式
*/
File[] files = file.listFiles(f -> {
return f.getName().endsWith(".java") || f.isDirectory();
});
for (File f:files){
if (f.isFile()){
System.out.println("文件名"+f.getAbsolutePath());
}else{
print(f);
}
}
}
3.Lambda省略格式及使用前提
省略规则
在Lambda标准格式的基础上,使用省略写法的规则为:
- 小括号内参数的类型可以省略;
- 如果小括号内有且仅有一个参,则小括号可以省略;
- 如果大括号内有且仅有一个语句,则无论是否有返回值,都可以省略大括号、return关键字及语句分号。
使用前提
但是使用时有几个问题需要特别注意:
- 使用Lambda必须具有接口,且要求接口中有且仅有一个抽象方法。
无论是JDK内置的Runnable、Comparator接口还是自定义的接口,只有当接口中的抽象方法存在且唯一时,才可以使用Lambda。 - 使用Lambda必须具有上下文推断。
也就是方法的参数或局部变量类型必须为Lambda对应的接口类型,才能使用Lambda作为该接口的实例。