一、lambda表达式实例
提到lambda表达式,我们一般用的最多的就是foreach遍历List、Set、Queue、Map等。
/**
* 容器forEache遍历
*/
public static void forEach() {
//list
List<String> list = new ArrayList<>();
list.forEach(s -> System.out.println(s));
//set
Set<String> set = new HashSet<>();
set.forEach(s -> System.out.println(s));
//queue
Queue<String> queue = new PriorityQueue();
queue.forEach(s -> System.out.println(s));
//deque
Deque<String> deque = new LinkedList();
deque.forEach(s -> System.out.println(s));
//Map
Map<String, Object> map = new HashMap();
map.forEach((s, o) -> System.out.println(s + ":" + o));
}
当然,我们可以自己实现写一个函数式接口,然后通过lambda表达式来编写我们自己的代码。
public static void main(String[] args) {
fooFun("测试打印效果",target -> System.out.println(target));
}
/**
* 随意一个方法
* @param target 需要被答应的内容
* @param printer 打印机
*/
public static void fooFun(String target,FunctionInterface printer){
printer.print(target);
}
/**
* 函数式接口,@FunctionalInterface可加可不加,FunctionalInterface属于target=Runtime
* ,编译时已经被jvm处理过
*/
@FunctionalInterface
public interface FunctionInterface<T>{
/**
* 接口函数
* @param target 需要被打印的目标
*/
void print(T target);
}
分析:
1.定义一个函数式接口(对应的,public interface FunctionInterface<T>);
2.在自己定义的方法中,传入这个已经定义好的函数式接口(fooFun(String target,FunctionInterface printer););
3在使用方法时,就能用lambda表达表达式了。(fooFun("测试打印效果",target -> System.out.println(target));)
二、什么是lambda表达式
这里演示了lambda表达式的基本使用,接下来我们来看看什么是lambda表达式。
lambda表达式我们可以理解为传递匿名函数的一种方式,这种表达式没有方法名,有参数列表,有方法体,有返回值,可能有异常抛出(通常不推荐抛出异常,java默认实现也没有抛出异常,只有自己实现时,才会根据需要抛出),能够当作参数带入方法中。

总结起来分两种:
(parameters) -> expression
(parameters) -> {statements;}
三、函数式接口、接口函数、静态方法、默认方法、方法引用
上文提到了函数式接口,接口方法,接下来我们了解一下什么是函数式接口,什么是接口方法。
函数式接口就是只定义了一个抽象方法的接口。
/**
* @author baopz
*/
@FunctionalInterface
public interface FunctionInterface {
@Override
int hashCode();
@Override
boolean equals(Object obj);
@Override
String toString();
/**
* 唯一的抽象方法
*/
void test();
/**
* 默认方法
*/
default void foo() {}
/**
* 静态方法
*/
static void bar() {}
}
分析:
1.代码中,接口Override了来自Object的三个方法hashCode()、equals()、toString()
2.唯一的抽象方法test(),用来在lambda表达式中唯一实现的方法,也就是接口函数
3.有个带有方法体的,由default修饰的默认方法foo(),可以理解为实现了该接口的子类,会顺带继承的方法,默认方法
4.有个带有方法体的,由static修饰的静态方法bar(),可以理解为工具方法,静态方法。
方法引用,仅仅涉及到单一方法的使用时,提供的一种更加简洁的写法的语法糖。
四、方法引用
指向静态方法的方法引用,Integer::parseInt
指向任意类型实例方法的引用,String::length
指向现有对象的实例方法的方法引用,methodReferences::length
构造函数、数组构造函数和父类调用(super-call)的一些特殊形式的方法引用
实例:
/**
* 方法引用事例
* @author baopz
*/
public class MethodReferences {
public static void main(String[] args) {
//静态方法的方法引用
staticFun("123", Integer::parseInt);
//任意类型实例方法的引用
instanceFun("foo", String::length);
//构造函数
MethodReferences methodReferences = createInstance(MethodReferences::new);
//数组构造函数
MethodReferences[] methodRes = createArrayInstance(3, MethodReferences[]::new);
//现有对象的实例方法的方法引用
curInstanceFun("foo", methodReferences::length);
}
public static void staticFun(String str, Consumer<String> consumer) {
consumer.accept(str);
}
public static void instanceFun(String str, Consumer<String> consumer) {
consumer.accept(str);
}
public static MethodReferences createInstance(Supplier<MethodReferences> supplier) {
return supplier.get();
}
public static MethodReferences[] createArrayInstance(int length, Function<Integer, MethodReferences[]> function) {
return function.apply(length);
}
public static int curInstanceFun(String str, Function<String, Integer> function) {
return function.apply(str);
}
public int length(String str) {
return str.length();
}
}
五、java默认提供的几类函数式接口
上文中使用了Function,Supplier,Consumer等接口,这些接口都来自,java1.8 新增的java.util.function包中,是java自带的一些默认函数式接口。
```
Predicate<T> T->boolean
Function<T,R> T->R
Consumer<T> T-void
Supplier<T> void -> T
...
数值类型函数式接口
```
* Predicate<T>,谓词,语义是传入任意一个类型T,经过处理(Predicate的接口方法test()),返回一个boolean类型的值。
public static boolean isDog(Dog dog, Predicate<Dog> predicate){
return predicate.test(dog);
}
public static void main(String[] args) {
Dog.isDog(new Dog(),dog -> dog instanceof Dog);
}
* Function<T,R> ,方法,语义是传入一个任意类型T,经过处理(Function 的接口方法apply()),返回一个R类型的值
public static void main(String[] args) {
//基础lambda表达式
Bar.length("foo",s -> s.length());
//方法引用的写法
Bar.length("foo",String::length);
}
public static int length(String foo, Function<String,Integer> function){
return function.apply(foo);
}
* Consumer<T>,消费者,语义是传入一个任意类型的T,用方法处理(Consumer的接口方法accept()),返回一个void(就是没有返回值)。
public static void main(String[] args) {
Dog.run(new Dog(),dog -> System.out.println("dog running."));
}
public static void run(Dog dog, Consumer<Dog> consumer){
consumer.accept(dog);
}
* Supplier<T>,提供者,语义是不用传递任何参数,直接通过方法(Supplier的接口方法get())产生一个T类型,并返回。
public static void main(String[] args) {
//lambda基本表达式
Dog dog = Dog.get(() -> new Dog());
//方法引用写法
dog = Dog.get(Dog::new);
}
public static Dog get(Supplier<Dog> supplier){
return supplier.get();
}
数值类型函数式接口,我们都知道在java中对象类型分两类,基础类型和引用类型,得益于java1.5提供的类型自动装箱,自动拆箱,用Integer、Double、Long等计算是拆箱为int、double、long等来进行数值计算,但自动装拆箱有效率损耗,所以lambda为了消除这种效率损耗,提供了数值类型函数式接口,如IntFunction、DoublePredicate、LongConsumer等(还有很多)
public static void main(String[] args) {
add(10, 20, (left, right) -> left + right);
}
public static int add(int a, int b, IntBinaryOperator operator) {
return operator.applyAsInt(a, b);
}
这里类型比较多,就不一一介绍,建议各位小伙伴如果要深入学习,建议把所有的类型都练习一遍。
由于类型比较多,类名不容易记住,java对这些类的命名有个小诀窍,
就是针对专门的输入参数类型的函数式接口,
1.名称都要加上对应的原始类型前缀(DoubleConsumer),
2.如果输入参数有两个的,都有Binary关键字(LongBinaryOperator),
3.如果没有具体类型的两个参数类型传入,则是以Bi为前缀(BiConsumer)
4.如果只知道返回类型的,都是以To为前缀(ToIntFunction)
六、思考
java1.8的lambda内容讲完了这里给大家留个小问题,如果传入的参数是三个或多个,该如何处理呢?