JDK8新特性
1.lambda表达式
概念:
- Lambda表达式: 特殊的匿名内部类,语法更简洁
- Lambda表达式允许把函数作为一个方法的参数(函数作为方法参数传递),将代码像数据一样传递。
语法:
<函数时接口><变量名>=(参数1, 参数2...)->{
//方法体
}
注意: 函数式接口: 接口中只有一个抽象方法。
(参数1,参数2):抽象方法的参数
->: 分隔符
{}:表示抽象方法的实现
例子:
public class Test {
public static void main(String[] args) {
MyRunnable my=new MyRunnable();
Thread t1=new Thread(my);
t1.start();
//匿名内部类
Runna匿名内部类ble r=new Runnable() {
@Override
public void run() {
System.out.println("匿名内部类任务接口");
}
};
Thread t2=new Thread(r);
t2.start();
}
}
class MyRunnable implements Runnable{
@Override
public void run() {
System.out.println("自定义任务接口");
}
}
分析代码:
- Thread 类需要Runnable接口作为参数,其中的抽象run方法是用来指定线程任务内容的核心
- 为了指定run的方法体,不得不需要Runnable接口的实现类
- 为了省去定义一个Runnable实现类的麻烦,不得不使用匿名内部类
- 必须覆盖重写抽象run方法,所以方法名称、方法参数、方法返回值不得不再写一遍,且不能写错
- 而实际上,似乎只有方法体才是关键所在。
遇到这样的情况可以使用lambda表达式完成上面的要求
前提:必须是函数式接口
//lambda表达式
Runnable r1=()->{
System.out.println("lambda表达式");
};
Thread t3=new Thread(r1);
t3.start();
1.1 无参返回值
public class Test02 {
public static void main(String[] args) {
//匿名内部类
Swimmable swimmable=new Swimmable() {
@Override
public void swimming() {
System.out.println("使用匿名内部类");
}
};
fun(swimmable);
//lambda表达式
// Swimmable s= () -> System.out.println("使用lambda表达式");
fun(() -> System.out.println("使用lambda表达式"));
}
public static void fun(Swimmable s){
s.swimming();
}
}
interface Swimmable{
public void swimming();
}
1.2 有参数有返回值的Lambda
下面举例演示java.util.Comparator接口的使用场景代码,其中的抽象方法定义为:
- public abstract int compare(T o1, T o2);
当需要对一个对象集合进行排序时,Collections.sort方法需要一个Comparator 接口实例来指定排
序的规则。
public class Test03 {
public static void main(String[] args) {
List<Person> personList=new ArrayList<>();
personList.add(new Person("A",15));
personList.add(new Person("B",17));
personList.add(new Person("C",16));
/*Comparator<Person> comparator=new Comparator<Person>() {
@Override
//int:0表示新的和原来的相同;1:o2比o1大;-1:o2比o1小
public int compare(Person o1, Person o2) {
return o2.getAge()-o1.getAge();
}
};*/
//lambda表达式
Comparator<Person> comparator=(Person o1, Person o2)->{
return o2.getAge()-o1.getAge();
};
Collections.sort(personList,comparator);
for (Person person : personList) {
System.out.println(person);
}
}
}
@Data
@AllArgsConstructor
@NoArgsConstructor
class Person{
private String name;
private int age;
}
1.3详细介绍lambda表达式
- Lambda引入了新的操作符:->(箭头操作符),->将表达式分成两部分
- 左侧:(参数1,参数2…)表示参数列表
- 右侧:{}内部是方法体
- 注意事项
- 形参列表的数据类型会自动推断
- 如果形参列表为空,只需保留()
- 如果形参只有1个,()可以省略,只需要参数的名称即可
- 如果执行语句只有一句,且无返回值,{}可以省略,若有返回值,则若想省去{},则必须同时省略return,且执行语句也保证只有一句
- Lambda不会生成一个单独的内部类文件
2. 函数式接口
- 如果一个接口只有一个抽象方法,则该接口称之为函数式接口,函数式接口可以使用Lambda表达式,Lambda表达式会被匹配到这个抽象方法上。
- **@Functionallnterface ** 注解检测接口是否符合函数式接口。
内置函数式接口的由来
public class Test {
public static void main(String[] args) {
Operator o=arr -> {
int sum=0;
for (int i : arr) {
sum+=i;
}
System.out.println("数组的和为:"+sum);
};
fun(o);
}
public static void fun(Operator operator){
int[] arr={2,3,4,5,8,11};
operator.getSum(arr);
}
}
@FunctionalInterface
interface Operator{
//求数组的和
public abstract void getSum(int[] arr);
}
分析
我们知道使用Lambda表达式的前提是需要有函数式接口。而Lambda使用时不关心接口名,抽象方
法名,只关心抽象方法的参数列表和返回值类型。因此为了让我们使用Lambda方便,JDK提供了大
量常用的函数式接口。
常见的函数式接口
都在java.util.function包下
4.1 Consumer
有参,无返回值
public class Test {
public static void main(String[] args) {
Consumer<Double> c= t->{
System.out.println("吃饭花了:"+t);
};
fun01(c,55);
}
//调用某个方法时,该方法需要的参数为接口类型,这时应该使用lambda
public static void fun01(Consumer<Double> consumer,double money){
consumer.accept(money);
}
}
4.2 Supplier供给型函数式接口
无参,有返回值
T get();
public class Test02 {
public static void main(String[] args) {
// Supplier<Integer> supplier= () -> new Random().nextInt(10);
fun(() -> new Random().nextInt(10));
}
public static void fun(Supplier<Integer> supplier){
Integer result = supplier.get();
System.out.println("内容为:"+result);
}
}
4.3 Function<T,R> 函数型函数式接口
T:参数类型的泛型
R:函数返回结果的泛型
有参,有返回值
例子:传入一个字符串把小写转换为大写
public class Test03 {
public static void main(String[] args) {
fun((t)-> t.toUpperCase(),"hello world");
}
public static void fun(Function<String,String> function,String msg){
String s = function.apply(msg);
System.out.println("结果为:"+s);
}
}
4.4 Predicate
T:参数的泛型
boolean test<T,t>
当传入一个参数时,需要对该参数进行判断时,则需要这种函数。
public class Test04 {
public static void main(String[] args) {
fun(t-> t.length()>3?true:false,"迪丽热巴");
}
public static void fun(Predicate<String> predicate,String name){
boolean b = predicate.test(name);
System.out.println("该名称的长度是否长啊:"+b);
}
}
5. 方法引用
方法引用的使用场景
我们用Lambda表达式来实现匿名方法。但有些情况下,我们用Lambda表达式仅仅是调用一些已经
存在的方法,除了调用方法外,没有其他任何多余的动作,在这种情况下,我们倾向于通过方法名来
调用它,而Lambda表达式可以帮助我们实现这一要求,它使得Lambda在调用那些已经拥有方法名
的方法的代码更简洁、更容易理解。方法引用可以理解为Lambda表达式的另外一种表现形式。
5.1 lambda表达式的冗余
public class Test01 {
public static void main(String[] args) {
Consumer<Integer[]> consumer=arr->{
int sum=0;
for (int i : arr) {
sum+=i;
}
System.out.println("数组的和为:"+sum);
};
fun(consumer);
}
public static void fun(Consumer<Integer[]> consumer){
Integer[] arr={1,2,3,4,5,6};
consumer.accept(arr);
}
public static void sum(Integer[] arr){
int sum=0;
for (int i : arr) {
sum+=i;
}
System.out.println("数组的和为:"+sum);
}
}
分析:
如果我们在Lambda中所指定的功能,已经有其他方法存在相同方案,那是否还有必要再写重复逻辑?可以直接"引用"过去就好了: --方法引用
public class Test01 {
public static void main(String[] args) {
Consumer<Integer[]> c=Test01::sum;
fun(c);
}
public static void fun(Consumer<Integer[]> consumer){
Integer[] arr={1,2,3,4,5,6};
consumer.accept(arr);
}
public static void sum(Integer[] arr){
int sum=0;
for (int i : arr) {
sum+=i;
}
System.out.println("数组的和为:"+sum);
}
}
请注意其中的双冒号 :: 写法,这被称为“方法引用”,是一种新的语法。
5.2 什么是方法引用
方法引用通过方法的名字来指向一个方法。 方法引用可以使语言的构造更紧凑简洁,减少冗余代码。 方法引用使用一对冒号 :: 。
方法引用的分类
类型 | 语法 | 对应的Lambda表达式 |
---|---|---|
静态方法引用 | 类名::staticMethod | (args)->类名.staticMethod(args) |
实例方法引用 | inst::instMethod | (args) -> inst.instMethod(args) |
对象方法引用 | 类名::instMethod | (inst,args) -> inst.instMethod(args) |
构建方法引用 | 类名::new | (args) -> new 类名(args) |
public class Test {
public static void main(String[] args) {
//void accept(T t);
// Consumer<String> consumer=t->{
// PrintStream out =System.out;
// out.println(t);
// };
//方法引用:如果Lambda表达方法体中只是调用一个特定得已经存在得方法,则可以使用方法引用
//对象::实例方法
// Consumer<String> consumer2=System.out::println;
// consumer2.accept("你是XXX?");
//类::静态方法 int compare(T o1,T o2);
// Comparator<Integer> comparator=(o1, o2) -> Integer.compare(o1,o2);
// Comparator<Integer> comparator=Integer::compare;
// int compare = comparator.compare(18, 18);
// System.out.println(compare);
//类名::实例方法 R apply(T t);
// Function<Emp,String> function=e->{
// return e.getName();
// };
// Function<Emp,String> function=Emp::getName;
// System.out.println(function.apply(new Emp("张三")));
//类名::new
// Supplier<Emp> supplier=new supplier<Emp>() {
// @Override
// public Emp get() {
// return new Emp("刘德华");
// }
// };
Supplier<Emp> supplier=Emp::new; //必须该类中存在无参构造函数。
System.out.println(supplier.get());
}
}