一:背景
在代码中看到了Lambda表达式和Cousume结合使用,针对这个在代码中的运行顺序有点看不懂执行顺序,因此了解下Lambda表达式在代码中的使用场景做一个简单研究。
二:使用场景
Lambda表达式是java8中引入新特性,其标准形式为:() ->{};
变体形式包括:
(x,y)->{return x +y;} //省略参数类型
(x,y) -> x +y //省略函数体的括号和 return
x -> x*x //省略参数括号和函数体的大括号和 return
()中是参数,{}中是运行表达式。示例如下
ini
Thread thread1 = new Thread(
() ->{System.out.println(Thread.currentThread().getId() +"11111");}
);
thread1.start();
场景1:替代匿名内部类
scss
//采用匿名内部类实现线程中方法运行
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getId() + " 11111");
}
});
thread.start();
//使用lambda替缓匿名内部类
Thread thread1 = new Thread(
() ->{System.out.println(Thread.currentThread().getId() +" 11111");}
);
thread1.start();
使用场景2:结合stream api进行集合元素处理
csharp
//forEach 循环读取list每一项
List<String> list1 = Arrays.asList("aaa","bbb");
//循环读取每一项并打印,采用标准for循环或者增强for都行
for (int i = 0;i<list1.size();i++){
System.out.println(list1.get(i));
}
//foreach要是换成匿名内部类的实现方式如下,和上面标准for循环一样效果,可进一步将匿名内部类改造成Lambda方式
list1.forEach(new Consumer<String>() {
@Override
public void accept(String s) {
System.out.println(s);
}
});
//采用forEach方式,同样实现循环读取打印每一个,和上面一样效果,采用lambda效果
list1.forEach((s)->{System.out.println(s);});
引申场景1:
forEach函数接受一个Cousume对象,Counsumer类定义中有一个accept函数,因此其实foreach函数接受的就是一个抽象接口对象。
csharp
default void forEach(Consumer<? super T> action) {
Objects.requireNonNull(action);
for (T t : this) {
action.accept(t);
}
}
public interface Consumer<T> {
void accept(T t);
}
以上场景其实就是典型的接口中传入一个匿名对象,然后该函数调用匿名函数的方法,所以从写法上等价于
typescript
List<String> list1 = Arrays.asList("aaa","bbb");
list1.forEach((s)->{System.out.println(s);});
//和上面的lambda表达式写法一样,上面就是匿名内部类的简化写法
list1.forEach(new Consumer<String>() {
@Override
public void accept(String s) {
System.out.println(s);
}
});
所以Consumer就是提供了空接口的类,如果要使用lamba表达式来实现一些逻辑时就需要有一个类似的接口类,供用户来直接使用,因此java 提供了Consumer这个接口类,在实际使用过程中可以Consumer作为参数放在接口中,这样就能直接在调用处直接将返回方法传递到函数中,就像传递了一个匿名内部类一样。
引申场景2:
在看到Consumer对象时,进一步可引申出java函数式编程,java函数式编程的主要几个实现接口类为:
Function,Consumer,Supplier,Predicate。这4个类各自有各自的作用,此处如何使用不详细展开,此4个类就是java中函数式编程的支持API。
在函数式编程中还引入了另外一个Optional类,该类的主要几个使用方法是
typescript
//判断值是否为空,不为空值则返回执行一个动作,为空则不执行
public void ifPresent(Consumer<? super T> action) {
if (value != null) {
action.accept(value);
}
}
//获取到Optional内包含的那个对象的值,如果为空则返回orElse中的另外值
public T orElse(T other) {
return value != null ? value : other;
}
//将Optional中的那个值进行map映射,映射规则就是map中传入的函数 可能出现返回Optional<Optional<U>>
public <U> Optional<U> map(Function<? super T, ? extends U> mapper) {
Objects.requireNonNull(mapper);
if (!isPresent()) {
return empty();
} else {
return Optional.ofNullable(mapper.apply(value));
}
}
//将Optional中的那个值进行map映射,映射规则就是map中传入的函数 但是返回的是一个没有嵌套的Option<U> 对象
public <U> Optional<U> flatMap(Function<? super T, ? extends Optional<? extends U>> mapper) {
Objects.requireNonNull(mapper);
if (!isPresent()) {
return empty();
} else {
@SuppressWarnings("unchecked")
Optional<U> r = (Optional<U>) mapper.apply(value);
return Objects.requireNonNull(r);
}
}
Optional的简单引用实例处理:
rust
public static void testLambda4(){
//通过ofNullable产生一个Optional对象
Optional<String> op = Optional.ofNullable(getName());
//通过ifPresent判断 如果op包含的对象不为空时,则执行这个函数对象
op.ifPresent((s) ->{System.out.println(s);});
//使用map函数将Optional中包的对象执行一次toUpperCase操作,
//且执行完之后如果返回的Optional不为空的话则执行 函数对象
op.map(String::toUpperCase).ifPresent((s)->{System.out.println("装换完成的"+s);});
//map函数的另外一种写法,即使用标准lambda表达式作为参数传入给到map函数,和上一行的函数等效
//map函数中的入参是一个函数式接口Function,此处应该传入的lambda表达式应该包含一个返回值,
//为什么是一个返回值的函数不是一个返回为void的函数,是根据map函数的定义的Function中的泛型接口确定的
op.map( (s) ->{
return s.toUpperCase();
}
).ifPresent((s)->{System.out.println("装换完成的"+s);} );
//map函数和flatmap函数的区别,返回值是否会嵌套返回Optional对象
Optional<Optional<String>> s2 = op.map((s) -> {
return Optional.ofNullable(s.toUpperCase());
});
Optional<String> s1 = op.flatMap(s -> {
return Optional.ofNullable(s.toUpperCase());});
}
public static String getName(){
return "aaa";
}