Lambda表达式

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有三点非常重要的特性:

  1. Stream 是不会存储元素的。

  2. Stream 不会改变原对象,相反,他们会返回一个持有结果的新Stream。

  3. Stream 操作是延迟执行的。意味着它们会等到需要结果的时候才执行。

Stream生成

Collection系的 stream() 和 parallelStream()

1

2

3

List<String> list = new ArrayList<>();

Stream<String> stream = list.stream();

Stream<String> stringStream = list.parallelStream();

  通过Arrays.stram()

1

Stream<String> stream1 = Arrays.stream(new String[10]);

  通过Stream.of()

1

Stream<Integer> stream2 = Stream.of(123);

  通过Stream.iterate()生成无限流

1

2

Stream<Integer> iterate = Stream.iterate(0, (x) -> x + 2);

iterate.limit(10).forEach(System.out::println);

  通过Stream.generate()

1

Stream<Double> generate = Stream.generate(() -> Math.random());generate.forEach(System.out::println);

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值