jdk1.8新特性知识点:
一、lambda表达式
lambda表达式为匿名内部类的简写,类似于匿名内部类的语法糖;但又区别于匿名内部类(后文会讲解)。
匿名内部类特点:
基于多态(多数基于接口编程)
实现类无需名称
允许多个抽象方法
Lambda的语法简洁,没有面向对象复杂的束缚。
特点:
使用Lambda必须有接口,并且接口中有且仅有一个抽象方法。
只有当接口中的抽象方法存在且唯一时,才可以使用Lambda,但排除接口默认方法以及声明中覆盖Object的公开方法。
使用Lambda必须具有上下文推断。
也就是方法的参数或局部变量类型必须为Lambda对应的接口类型,才能使用Lambda作为该接口的实例。
备注:有且仅有一个抽象方法的接口,称为“函数式接口”。
标准格式由三部分组成:
一些参数
一个箭头
一段代码
格式:
(参数列表)->{一些重要方法的代码};
():接口中抽象方法的参数列表,没有参数,就空着;有参数就写出参数,多个参数用逗号分隔。
->:传递:把参数传递给方法体{}
{}:重写接口的抽象方法的方法体
箭头操作符的左侧对应接口中参数列表(lambda表达式的参数列表),
箭头右侧为该抽象方法的实现(lambda表达式所需执行的功能)。
**lambda优化写法:**
可以推导的,都可以省略(凡是能根据上下文推导出来的内容,都可以省略不写):
(参数列表):括号中参数列表的数据类型,可以省略不写
(参数列表):括号中的参数只有一个,那么类型和()都可以省略
{一些代码} :如果{}中的代码只有一行,无论是否有返回值,都可以省略({},return,分号)
注意:要省略{},return,分号 必须一起省略
public class MyLambda {
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();
//优化lambda
new Thread(()->System.out.println(Thread.currentThread().getName()+"新线程创建了")).start();
}
}
1.1无参数,无返回
()->System.out.println("hello lambda")
Cook:
public interface Cook {
public abstract void makeFood();
}
public class Demo01Cook {
public static void main(String[] args) {
invokeCook(new Cook() {
public void makeFood() {
System.out.println("做饭。。。");
}
});
//使用Lambda
invokeCook(()->{
System.out.println("做饭。。。");
});
//优化lambda
invokeCook(()-> System.out.println("做饭。。。"));
}
public static void invokeCook(Cook cook){
cook.makeFood();
}
}
1.2 有参数,无返回
x->System.out.println("hello lambda")
import java.util.function.Consumer;
/**
* Created by hongcaixia on 2019/10/31.
*/
public class Demo2 {
public static void main(String[] args) {
Consumer<String> consumer = x-> System.out.println(x);
consumer.accept("有参数无返回");
}
}
1.3 有参数,有返回
(Person p1,Person p2)->{ return p1.getAge()-p2.getAge(); }
package com.hcx.lambda;
import java.util.Arrays;
import java.util.Comparator;
/**
* Created by hongcaixia on 2019/10/26.
*/
public class MyArrays {
public static void main(String[] args) {
Person[] arr = {new Person("陈奕迅",40),
new Person("钟汉良",39),
new Person("杨千嬅",38)};
//对年龄进行排序
Arrays.sort(arr, new Comparator<Person>() {
@Override
public int compare(Person o1, Person o2) {
return o1.getAge()-o2.getAge();
}
});
//使用lambda
Arrays.sort(arr,(Person p1,Person p2)->{
return p1.getAge()-p2.getAge();
});
//优化lambda
Arrays.sort(arr,(p1,p2)->p1.getAge()-p2.getAge());
Arrays.sort(arr,Comparator.comparing(Person::getAge));
for (Person p:arr){
System.out.println(p);
}
}
}
Lmabda表达式的语法总结: () -> ();
前置 | 语法 |
---|---|
无参数无返回值 | () -> System.out.println(“Hello WOrld”) |
有一个参数无返回值 | (x) -> System.out.println(x) |
有且只有一个参数无返回值 | x -> System.out.println(x) |
有多个参数,有返回值,有多条lambda体语句 | (x,y) -> {System.out.println(“xxx”);return xxxx;} |
有多个参数,有返回值,只有一条lambda体语句 | (x,y) -> xxxx |
口诀:左右遇一省括号,左侧推断类型省
注:当一个接口中存在多个抽象方法时,如果使用lambda表达式,并不能智能匹配对应的抽象方法,因此引入了函数式接口的概念
二、函数式接口
一、概念
函数式接口在Java中是指:有且仅有一个抽象方法的接口。
二、格式
只要确保接口中有且仅有一个抽象方法即可:
修饰符 interface 接口名称 {
public abstract 返回值类型 方法名称(可选参数信息);
}
由于接口当中抽象方法的public abstract是可以省略的,所以定义一个函数式接口很简单:
public interface MyFunctionalInterface {
//抽象方法只有一个
void method();
//可以含有默认方法
public default void method_1() {
}
}
自定义函数式接口(无参无返回值)
对于刚刚定义好的MyFunctionalInterface函数式接口,典型使用场景就是作为方法的参数:
public class Demo04 {
public static void main(String[] args) {
show(() -> System.out.println("lambda执行了。。。。"));
}
public static void show(MyFunctionalInterface mi) {
mi.method();// 调用自定义的函数式接口方法
}
}
自定义函数式接口(有参有返回)
请定义一个函数式接口Sumable,内含抽象sum方法,可以将两个int数字相加返回int结果。使用该接口作为方法的参数,并进而通过Lambda来使用它。
public interface Sumable {
int sum(int a, int b);
}
public class Demo05 {
public static void main(String[] args) {
getSum(150,250,(a,b) -> a + b);
}
private static void getSum(int a,int b,Sumable sumable){
int sum = sumable.sum(a, b);
System.out.println(sum);
}
}
三、jdk1.8–方法引用
当要传递给Lambda体的操作,已经有实现的方法了,可以使用方法引用!(实现抽象方法的参数列表,必须与方法引用方法的参数列表保持一致!)方法引用:使用操作符 “::” 将方法名和对象或类的名字分隔开来
可以将方法引用理解为 Lambda 表达式的另外一种表现形
如下三种主要使用情况:
1、对象引用::实例方法名
2、类::静态方法名
3、类::实例方法名(lambda参数列表中第一个参数是实例方法的调用者,第二个参数是实例方法的参数时可用)**
public void test() {
/*注意:
*1.lambda体中调用方法的参数列表与返回值类型,要与函数式接口中抽象方法的函数列表和返回值类型保持一致
*2.若lambda参数列表中的第一个参数是实列方法的调用者,而第二个参数是实例方法的参数时,可以使用ClassName::meth
*/
Consumer<Integer> con = (x) -> System.out.println(x);
con.accept(100);
// 方法引用-对象::实例方法
Consumer<Integer> con2 = System.out::println;
con2.accept(200);
// 方法引用-类名::静态方法名
BiFunction<Integer, Integer, Integer> biFun = (x, y) -> Integer.compare(x, y);
BiFunction<Integer, Integer, Integer> biFun2 = Integer::compare;
Integer result = biFun2.apply(100, 200);
// 方法引用-类名::实例方法名
BiFunction<String, String, Boolean> fun1 = (str1, str2) -> str1.equals(str2);
BiFunction<String, String, Boolean> fun2 = String::equals;
Boolean result2 = fun2.apply("hello", "world");
System.out.println(result2);
}
(b)构造器引用
格式:ClassName::new
public void test2() {
// 构造方法引用 类名::new
Supplier<Employee> sup = () -> new Employee();
System.out.println(sup.get());
Supplier<Employee> sup2 = Employee::new;
System.out.println(sup2.get());
// 构造方法引用 类名::new (带一个参数)
Function<Integer, Employee> fun = (x) -> new Employee(x);
Function<Integer, Employee> fun2 = Employee::new;
System.out.println(fun2.apply(100));
}
c)数组引用
格式:Type[]::new
public void test(){
// 数组引用
Function<Integer, String[]> fun = (x) -> new String[x];
Function<Integer, String[]> fun2 = String[]::new;
String[] strArray = fun2.apply(10);
Arrays.stream(strArray).forEach(System.out::println);
}