1、Java8的Lambda特性
1、Lambda 表达式,也可称为闭包,它是推动 Java 8 发布的最重要新特性。
2、Lambda 允许把函数作为一个方法的参数(函数作为参数传递进方法中)。
3、使用 Lambda 表达式可以使代码变的更加简洁紧
2、Lambda表达式的语法
(arg1, arg2...) -> { body }
(type1 arg1, type2 arg2...) -> { body }
比如:
(int a, int b) -> { return a + b; }
() -> System.out.println("Hello World");
(String s) -> { System.out.println(s); }
(): 括号就是接口方法的括号,接口方法如果有参数,也需要写参数。只有一个参数时,括号可以省略。
->: 分割左右部分的,没啥好说的。
{} : 要实现的方法体。只有一行代码时,可以不加括号,可以不写return。
3、Lambda表达式的特征
- 可选类型声明:不需要声明参数类型,编译器可以统一识别参数值
- 可选的参数圆括号:一个参数无需定义圆括号,但多个参数需要定义圆括号。
- 可选的大括号:如果主体包含了一个语句,就不需要使用大括号
- 可选的返回关键字
4、Lambda变量作用域
只能引用外部被final
修饰的局部变量,换句话说在Lambda
表达式中我们不能修改定义在外的局部变量
5、函数式接口的定义与特性、
函数式接口(FunctionalImplement)是一种只含有一个抽象方法声明的接口,
但是可以有多个非抽象方法的接口。类似于Java中的Marker Interface标记类型接口,
比如java.io.Serializable等都是没有方法声明或属性声明的接口,主要用于通过instanceof就直接能探测一个实例是否是一个特定接口的实例。
那么java.lang.Runable即是一种函数式接口,在接口中只有void run()方法。
简单来说之前我们使用的匿名内部类传入参数,实则是使用匿名内部类来实例化函数式接口的对象。而之后的Lambda表达式可以更进一步简化代码
看到这里你也许意识到,如果接口中有多个方法时,按照上面的逻辑lambda表达式恐怕不行了。没错,
lambda表达式只适用于函数型接口。说白了,函数型接口就是只有一个抽象方法的接口。
这种类型的接口还有一个对应的注解:@FunctionalInterface。
为了让我们在需要这种接口时不再自己去创建,Java8中内置了四大核心函数型接口。
消费型接口(有参无返回值)
Consumer<T>
void accept(T t);
供给型接口(无参有返回值)
Supplier<T>
T get();
函数型接口(有参有返回值)
Function<T, R>
R apply(T t);
断言型接口(有参有布尔返回值)
Predicate<T>
boolean test(T t);
java.util.function包下面有多个对函数式编程的API
函数式接口关注的仅是入参类型、出参类型和个数而已
Function和BiFunction都是是java8提供的API
固定格式如下:
一个入参写法:
Function<入参类型,出参类型> 指针 = 类/实例 :: 方法名字
出参数类型 xxx = 指针.apply(入参数值)
两个入参写法:
BiFunction<入参1类型,入参2类型,出参类型> 指针 = 类/实例 :: 方法名字
出参数类型 xxx = 指针.apply(入参数1值,入参数2值)
示例如下:
package thread.mythreadPool;
import java.util.function.BiFunction;
import java.util.function.Function;
public class Test {
/**
* 方法有静态方法(static)和非静态方法,
* 但函数式接口关注的仅是入参、出参类型和个数而已:
*
* @param str
* @return
*/
public Long noStaticFoo(String str) {
return Long.parseLong(str) * 30;
}
public static Long staticFoo(String str) {
return Long.parseLong(str) * 40;
}
public static void main(String[] args) {
//Function 对象staticMath就是指向类Test的静态方法的指针
下面的双冒号就是固定的写法
Function<String, Long> staticMath = Test::staticFoo;
//只要调用指针的apply方法,就可以执行具体的方法了
Long apply1 = staticMath.apply("23");
System.out.println(apply1);
// 静态方法,直接用类::
Function<String, Long> staticFoo = Test::staticFoo;
Long apply = staticFoo.apply("100");
System.out.println(apply);
// 非静态方法,用类的实例对象::
Test test = new Test();
Function<String, Long> noStaticFoo = test::noStaticFoo;
Long apply2 = noStaticFoo.apply("100");
System.out.println(apply2);
/* 这种可以理解成两个入参、除了原来方法的入参外,还要指明实例对象(因为这是一个实例方法)*/
BiFunction<Test, String, Long> noStaticFooTest = Test::noStaticFoo;
Long apply3 = noStaticFooTest.apply(test, "100");
System.out.println(apply3);
}
}
Lambda表达式还有两种简化代码的手段,分别是方法引用、构造引用
方法引用
方法引用是什么呢?如果我们要实现接口的方法与另一个方法A类似,(这里的类似是指参数类型与返回值部分相同),我们直接声明A方法即可。也就是,不再使用lambda表达式的标准形式,改用高级形式。
无论是标准形式还是高级形式,都是lambda表达式的一种表现形式。
Function function1 = (x) -> x;
Function function2 = String::valueOf;
对比Function接口的抽象方法与String的value方法,可以看到它们是类似的。
R apply(T t);//有参有返回
public
static
String valueOf(Object obj) {
return
(obj ==
null
) ?
"null"
: obj.toString();
}
方法引用的语法:
对象::实例方法
类::静态方法
类::实例方法
构造引用
先来创建一个供给型接口对象
Supplier<String> supplier = () ->
new
String();
在这个lammbda表达式中只做了一件事,就是返回一个新的String对象,而这种形式可以更简化:
Supplier<String> supplier = String::
new
;
上面并没有给出太多的lambda实例,只是侧重讲了如何去理解lambda表达式。到这里,不要懵。要记住
lambda的本质:为函数型接口的匿名实现进行简化与更简化
所谓的简化:就是lambda的标准形式,
所谓的更简化:是在标准形式的基础上进行方法引用和构造引用。
方法引用:是拿已有的方法去实现此刻的接口。
构造引用:是对方法体只有一句new Object()的进一步简化。
允许把函数做个一个方法的参数(函数作为参数传入方法中)
当一个函数/方法 可以被变量引用时,就可以做很多事情了,
好好理解上面这句话的含义------
举例业务场景:
在A B C 三个业务里都到了 服务userHttpService.getById(id)【内部实现是http请求去第三方调用】
只是在A和B业务中只有这一个userHttpService.getById请求用到了http的调用
但是在C业务中有多个请求是调用http接口远程服务,所有想要用多线程来异步执行userHttpService.getById请求
思路:
- 常规思路----直接修改userHttpService的getByid这个请求为异步,
结果:改动3处,只要是调用的地方ABC三个地方都需要改动,改为接受Future对象,并从服务中获取
pbuclic Future getUserByidRemote(int userId){
Future returnFure = executorService.submit((id)->{
User user = httpservice.getById(id);
return user;
});
}
- 更好的思路:因为A和B里面就一个http服务,所有并不需要多线程异步去执行,只有C是需要的,我只需要修改C的调用就好了
结果:只需要改动1处,A和B业务里的代码不动,只改动C的调用
实现方式:利用Lambda可以方法引用的特性
只需要写一个工具类即可,里面只需要借助java8提供的lambda的API
package thread.mythreadPool;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.function.Function;
public class AsyncExecutor {
// 线程池,建议恰当配置(拒绝策略建议CallerRunsPolicy)和使用框架注入
private ExecutorService executorService = Executors.newFixedThreadPool(10);
/**
* 单个入参,有返回值的异步执行方法 , public User getById(Long id)
*
* @param method 把方法做为参数(lambda的核心在这里),如 , userHttpService::getById
* @param paramValue 入参值,如 100 ---这里的名字可以随便起
* @param <paramType> 入参类型,如 Long ---这里的名字可以随便起
* @param <ReturnType> 返回值类型,如 User ---这里的名字可以随便起
* @return Future对象,用以判断是否执行结束、获取返回结果
*/
public <paramType, ReturnType> Future<ReturnType> async(
Function<paramType, ReturnType> method,
paramType paramValue) {
return executorService.submit(() -> method.apply(paramValue));
}
}
此时,在要想在A处异步调用 userHttpService::getById方法,只需要修改为这样写即可
核心东西在这里:lambde的高级写法:实例:方法 userHttpService::getById
class A {
@Autowired--自动注入(从spring容器中获取实例)
private AsyncExecutor asyncExecutor;
@Autowired
private UserHttpService userHttpService;
public void xxx(){
//原先的写法
// User user = userHttpService.getById(id);
//异步调用的写法
Future<User> userFuture = asyncExecutor.async(userHttpService::getById,id);
User user = userFuture.get();
}
}
对于其他的无入参、两入参、无返回值等的方法形式,也可以类似处理
public class AsyncExecutor {
// 线程池,建议恰当配置(拒绝策略建议CallerRunsPolicy)和使用框架注入
private ExecutorService executorService = Executors.newFixedThreadPool(10);
/**
* 无入参,无返回值的异步执行方法 , void noStaticFoo()
*
* @param method 要执行的方法,如 user::noStaticFoo;
* @return Future对象,用以判断是否执行结束
*/
public Future async(Runnable method) {
return executorService.submit(method);
}
/**
* 有单个入参,无返回值的异步执行方法,如 void noStaticFoo(Long id)
*
* @param method 要执行的方法,如, user::noStaticFoo
* @param param 方法执行的入参,如id
* @param <P> 入参类型,如Long
* @return Future对象,用以判断是否执行结束
*/
public <P> Future async(Consumer<P> method, P param) {
return executorService.submit(() -> method.accept(param));
}
/**
* 有两个参数但是无返回值的异步执行方法, 如void noStaticFoo(Long id,Entity entity)
*
* @param method 要执行的方法,如 , user::noStaticFoo
* @param param1 第一个入参值,如id
* @param param2 二个入参值,如entity
* @param <P1> 第一个入参类型
* @param <P2> 第二个入参类型
* @return Future对象,用以判断是否执行结束
*/
public <P1, P2> Future async(BiConsumer<P1, P2> method, P1 param1, P2 param2) {
return executorService.submit(() -> method.accept(param1, param2));
}
/**
* 无参数有返回值的异步执行方法 , Entity noStaticFoo()
*
* @param method 要执行的方法,如 , user::noStaticFoo
* @param <R> 返回值类型,如 Entity
* @return Future对象,用以判断是否执行结束、获取返回结果
*/
public <R> Future<R> async(Supplier<R> method) {
return executorService.submit(method::get);
}
/**
* 单个入参,有返回值的异步执行方法 , Entity noStaticFoo(Long id)
*
* @param method 要执行的方法,如 , user::noStaticFoo
* @param param 入参值,如 id
* @param <P> 入参类型,如Long
* @param <R> 返回值类型,如 Entity
* @return Future对象,用以判断是否执行结束、获取返回结果
*/
public <P, R> Future<R> async(Function<P, R> method, P param) {
return executorService.submit(() -> method.apply(param));
}
/**
* 两个入参,有返回值的异步执行方法 , Entity noStaticFoo(Long id)
*
* @param method 要执行的方法,如 , user::noStaticFoo
* @param param1 第一个入参值,如id
* @param param2 二个入参值,如entity
* @param <P1> 第一个入参类型
* @param <P2> 第二个入参类型
* @param <R> 返回值类型,如 Entity
* @return Future对象,用以判断是否执行结束、获取返回结果
*/
public <P1, P2, R> Future<R> async(BiFunction<P1, P2, R> method, P1 param1, P2 param2) {
return executorService.submit(() -> method.apply(param1, param2));
}
}
最后介绍一个steam
比如Streams的相关API,效率比反射中的Method要高
Stream理解
如何理解Stream?在我看来,Stream 不是集合元素,它不是数据结构并不保存数据,它是有关算法和计算的,它更像一个高级版本的 Iterator。简单来说,它的作用就是通过一系列操作将数据源(集合、数组)转化为想要的结果。
Stream有三点非常重要的特性:
-
Stream 是不会存储元素的。
-
Stream 不会改变原对象,相反,他们会返回一个持有结果的新Stream。
-
Stream 操作是延迟执行的。意味着它们会等到需要结果的时候才执行。
Stream生成
Collection系的 stream() 和 parallelStream()
1 2 3 |
|
通过Arrays.stram()
1 |
|
通过Stream.of()
1 |
|
通过Stream.iterate()生成无限流
1 2 |
|
通过Stream.generate()
1 |
|