Lambda表达式
匿名内部类仍然有很多冗余,为了更简洁,JDK8加入了Lambda表达式:
(参数类型 参数名称) -> { 代码 }
注意:
- Lambda必须具有接口,且接口中只有一个抽象方法
- Lambda必须有上下文才能推断,也就是局部变量等
比如:有个Person类,里面有name和age,做排序:
import java.util.Arrays;
public class Test {
public static void main(String[] args) {
Person[] arr = {
new Person("张三", 19),
new Person("李四", 18)};
Arrays.sort(arr, (Person a, Person b) -> {
return a.getAge() - b.getAge();
});
for (Person p : arr) {
System.out.println(p.getName() + " " + p.getAge());
}
}
}
省略格式:
- 小括号内参数可以省略
- 如果只有一个参数,括号也可以省略
- 如果代码只有一行,花括号/return/分号都可以省略
函数式接口
函数式接口:有且仅有一个抽象方法的接口,也就是Lambda使用的接口。
特点:
- 函数前面加一个
@FunctionalInterface
叫注解,用于检查接口是否是一个函数式接口。 - 函数式接口一般作为方法的参数或返回值
- 匿名内部类会创建一个.class文件,加载到内存;Lambda表达式不会;
如,函数式接口
@FunctionalInterface
interface MyFunctionInterface {
public abstract void method();
}
函数式编程:函数式接口作为方法的参数
public class Test {
public static void main(String[] args) {
startThread(() -> System.out.println("开启线程任务"));
}
public static void startThread(Runnable run) {
new Thread(run).start();
}
}
java.util.function
包中提供了大量的函数式接口:
Supplier
:传入一个类型的数据 ,返回该类型数据Consumer
:不生产数据,而是消费数据Predicate
:对数据判断,返回一个布尔值Function
:输入一个数据类型,转换成另一个数据类型
Stream流
Stream流和IO流不一样,JDK1.8之后,提出Stream流,是为了解决已有集合类库的弊端。
public class Test {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("张三");
list.add("李四");
list.add("张阳");
list.add("王老五");
// 两个过滤,一个遍历
list.stream()
.filter(name->name.startsWith("张"))
.filter(name->name.length()==2)
.forEach(name-> System.out.println(name));
}
}
获取流:
- JDK8之后,在Collection中加入了default方法stream(),其子类可以直接调用。
- Stream接口的静态方法of可以获取数组对应的流。
流中的方法:
- 延迟方法:返回值仍是Stream流,可以链式编程
- 终结方法:
count()
和forEach()
。stream流在使用完终结方法,将不能再使用。
forEach遍历【终结方法】
Stream<String> stream = Stream.of("张三", "李四", "王五", "赵六");
// forEach方法接收一个Consumer接口函数, Consumer.accept()消费一个数据
stream.forEach((String name) -> System.out.println(name));
filter过滤
Stream<String> stream = Stream.of("张三", "李四", "张阳", "赵六");
// filter方法接收一个Predicate接口函数, Predicate.test()返回布尔值
Stream<String> stream2 = stream.filter((String name) -> name.startsWith("张"));
stream2.forEach((String name) -> System.out.println(name));
map类型转换
Stream<String> stream = Stream.of("1", "2", "3", "4");
// map方法接收一个Function接口函数, Function.apply()转换数据类型
Stream<Integer> stream2 = stream.map((String x) -> Integer.parseInt(x));
stream2.forEach((Integer x) -> System.out.println(x));
count统计个数【终结方法】
Stream<String> stream = Stream.of("1", "2", "2", "4");
System.out.println(stream.count());
limit截取前几个,skip跳过前几个
Stream<String> stream = Stream.of("张三", "李四", "王五", "赵六");
Stream<String> stream2 = stream.limit(3).skip(1);
stream2.forEach((name) -> System.out.println(name));
concat合并两个流
Stream<String> stream = Stream.of("张三");
Stream<String> stream2 = Stream.of("老王");
Stream<String> stream3 = Stream.concat(stream, stream2);
stream3.forEach((name) -> System.out.println(name));
方法引用
Lambda表达式:拿什么参数做什么操作。如果操作方案已经存在,那就没有必要再写一遍 。
方法引用:用双冒号::
表示引用运算符,它所在的表达式表示方法引用。如果Lambda要表达的函数方案存在于某个方法的实现中,那么可以通过双冒号来引用该方法作为Lambda的替代者。
例如,System.out
对象中有一个重载的println(s)
方法是我们需要的,那么下面两种写法等效:
- Lambda表达式:
(String s) -> System.out.println(s)
,表示参数通过Lambda传递给println - 方法引用:
Systeml.out::println
,表示参数直接用println打印
函数式接口是Lambda的基础,方法引用是Lambda的孪生兄弟。
通过对象名引用成员方法:
// Lambda表达式
printString((s) -> {
MethodRerObject obj = new MethodRerObject();
obj.printUpperCaseString(s);
});
// 方法引用:有对象,有对象中的方法,那么直接引用该方法
MethodRerObject obj2 = new MethodRerObject();
printString(obj2::printUpperCaseString);
通过类名引用静态成员方法:
// Math类中有一个abs()静态方法
method(Math::abs);
// 自己传递数
method2(-10, Math::abs);
通过super引用成员方法:
// 在子类内有一个方法,是调用父类的方法传递给函数式接口
// Lambda表达式
method(()->new FuClass().sayHello());
method(()->super.sayHello());
// 引用方法
method(super::sayHello);
通过this引用成员方法:
// 和用super差不多
// Lambda表达式
method(()->this.sayHello());
// 引用方法
method(this::sayHello);
类的构造器引用:
// 有一个类,以及类的函数式接口
printName("张三", (name) ‐> new Person(name));
// 方法引用
printName("张三", Person::new);
数组的构造器引用:
数组也是Object子类对象,也有构造器,用法和类的构造器一样。
public class Test {
public static void main(String[] args) {
// Lambda表达式
int[] array = initArray(10, (length) -> new int[length]);
// 方法引用
int[] array2 = initArray(10, int[]::new);
}
public static int[] initArray(int length, ArrayBuilder b) {
return b.buildArray(length);
}
}
// 函数式接口
@FunctionalInterface
interface ArrayBuilder {
int[] buildArray(int length);
}