在数学中,函数就是有输入量、输出量的一套计算方案,也就是“拿什么东西做什么事情”。相对而言,面向对象过分强调“必须通过对象形式来做事情”,而函数式思想则尽量忽略面向对象的复杂语法——强调做什么,而不是以什么形式做
**面向对象的思想:**做一件事情,找一个能解决这个事情的对象,调用对象的方法完成事情
**函数式编程思想:**只要能获取到结果,谁去做的,怎么做的都不重要,重视的是结果,不重视过程
原始代码:
RunnableImpl run=new RunnableImpl();
Thread th=new Thread(run);
th.start();
匿名内部类用法:
new Thread(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"又创建了");
}
}).start();
代码分析:
对于Runnable
的匿名内部类用法,可以分析出几点内容:
Thread
类需要Runnable
接口作为参数,其中的抽象run
方法是用来指定线程任务内容的核心run
的方法体,不得不需要Runnable
接口的实现类RunnableImpl
实现类的麻烦,不得不使用匿名内部类run
方法,所以方法名称、方法参数、方法返回值不得不重写一遍,且不能写错我们真的希望创建一个匿名内部类对象吗?不,我们只是伪类做这件事情而不得不创建一个对象,我们真正希望做的事情,是将run
方法体内的代码传递给Thread
类知晓
传递一段代码——这才是我们真正的目的。而创建对象只是受限于面向对象语法而不得不采取的一种手段方式。如果我们将关注点从“怎么做“回归到”做什么“的本质上,就会发现只要能够更好地达到目的,过程与形式其实并不重要
在2014年3月Oracle发布的Java8(JDK1.8)中,加入了Lambda表达式的重量级新特性
Lambda表达式创建多线程与匿名内部类创建多线程对比:
public class Lambda_Creat {
public static void main(String[] args) {
//使用匿名内部类方式实现多线程
new Thread(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}).start();
//使用Lambda表达式实现多线程
new Thread(()->{
System.out.println(Thread.currentThread().getName());
}).start();
}
}
仔细分析该代码中的语义,Runnable
接口只有一个run
方法的定义:
public abstract void run();
即制定了一种做事情的方案(其实就是一个函数):
同样的语义体现在Lanbda
语法中,要更加简单:
()->{System.out.println("多线程任务执行");}
run
方法的参数(无),代表不需要任何条件Lambda格式由三部分组成:
Lambda表达式的标准格式为:
(参数列表)->{一些重写方法的代码}
解释说明格式:
():接口中抽象方法的参数列表,没有参数,就空着;有参数就写出参数,多个参数使用逗号分隔
->:传递的意思,把参数传递给方法体{}
{}:重写接口的抽象方法的方法体
给定一个厨子Cook
接口,内含唯一的抽象方法makeFood
,且无参数、无返回值。如下:
public interface Cook{
void makeFood();
}
在下面的代码中,请使用Lambda的标准格式调用invokeCook
方法,打印输出“吃饭了”字样:
public class Cook_Main {
public static void main(String[] args) {
//请再此使用Lambda【标准格式】调用invokeCook方法
invokeCook(()->{
System.out.println("吃饭了");
});
}
private static void invokeCook(Cook cook){
cook.makeFood();
}
}
需求:
1、使用数组存储多个Person对象
2、对数组中的Person对象使用Arrays的sort方法通过年龄进行升序排序
public class Lambda_Main {
public static void main(String[] args) {
Person[] arr={
new Person("柳岩",18),
new Person("迪丽热巴",19),
new Person("古力娜扎",15)
};
//使用匿名内部类实现重写方法
Arrays.sort(arr, new Comparator<Person>() {
@Override
public int compare(Person o1, Person o2) {
return o1.getAge()-o2.getAge();
}
});
//使用Lambda表达式实现重写方法
Arrays.sort(arr,(Person o1,Person o2)->{
return o1.getAge()-o2.getAge();
});
for (Person person:arr) {
System.out.println(person);
}
}
}
题目:
给定一个计算器Calculator
接口,内含抽象方法calc
可以将两个int数字相加得到和值:
public interface Calculator{
int calc(int a,int b);
}
public class Main {
public static void main(String[] args) {
//请在此使用Lambda表达式【标准格式】调用invokeCalc方法计算120+130的结果
//使用匿名内部类
invokeCalc(120, 130, new Calculator() {
@Override
public int calc(int a, int b) {
return a+b;
}
});
//使用Lambda表达式
invokeCalc(120,130,(int a,int b)->{
return a+b;
});
}
private static void invokeCalc(int a,int b,Calculator calculator){
int result=calculator.calc(a,b);
System.out.println("结果是:"+result);
}
}
Lambda表达式:是可推导、可省略的
范式根据上下文推导出来的内容,都可以省略
可以省略的内容:
1、(参数列表):括号中的参数列表的数据类型可以省略
2、(参数列表):括号中的参数如果只有一个,那么类型和()都可以省略
3、(一些代码):如果{}中的代码只有一行,无论是否有返回值,都可以省略({},return,;分号)
1、使用Lambda必须具有接口,且要求接口中有且仅有一个抽象方法。
Runnable
、Comparator
接口还是自定义的接口,只有当接口中的抽象方法存在且唯一时,才可以使用Lambda2、使用Lambda必须具有上下文推断
也就是方法的参数或者局部变量类型必须为Lambda对应的接口类型,才能使用Lambda作为接口的实例
备注:有且仅有一个抽象方法的接口,称为“函数式接口”