JAVA8 Lamda函数

1 lamda函数概念

  简单理解为匿名函数:

   public int add(int x, int y) {
        return x + y;
    }

 转成 lamda函数:(int x, int y) -> x + y;  

 或者(x, y) -> x + y;编译器可以自动识别参数类型

2 lamda类型

lamda表达式的类型,叫做“目标类型(target type)”。lamda表达式的目标类型是“函数接口(functional interface)”Java8才引入。

一个接口,如果只有一个显式声明的抽象方法,那么它就是一个函数接口。

一般用@FunctionalInterface标注出来(也可以不标注,毕竟只有一个函数/方法)

为什么只能定义一个函数接口呢:  因为我们的lamda函数都是通用型的方法,编译器要能自动识别是哪个方法。

示例:

//这个是线程接口

@FunctionalInterface
    public interface Runnable { void run(); }

也可以申明多个接口,但其中一个接口必须是Object默认继承的(equals所有类接口都会继承Object),所以可以显示在lamda接口申明多个函数。

如下:
    public interface Comparator<T> { int compare(T o1, T o2); boolean equals(Object obj); }

根据接口类型,  编译器自动匹配方法。包括自定义的lamda接口,如

@FunctionalInterface
    public interface MyRunnable {
        public void run();
    }

下属写法都正确,  自动匹配对应的接口函数

  Runnable r1 =    () -> {System.out.println("Hello Lambda!");};
    MyRunnable2 r2 = () -> {System.out.println("Hello Lambda!");};

这说明一个λ表达式可以有多个目标类型(函数接口),只要函数匹配成功即可。

3 lamda表达式使用

3.1 内部匿名类,各种回调,比如事件响应器、传入Thread类的Runnable等

Thread oldSchool = new Thread( new Runnable () {
        @Override
        public void run() {
            System.out.println("This is from an anonymous class.");
        }
    } );
   
    Thread gaoDuanDaQiShangDangCi = new Thread( () -> {
        System.out.println("This is from an anonymous method (lambda exp).");
    } );

第二个为lamda表达式, 编译器会自动推断出来:一个Thread的构造函数接受一个Runnable参数,而传入的λ表达式正好符合其run()函数,所以Java编译器推断它为Runnable

3.2 集合类批处理操作

3.2.1)list

集合类的批处理操作API的目的是实现集合类的“内部迭代”(以前式用户自己写for循环自己迭代),并期望充分利用现代多核CPU进行并行计算。

for(Object o: list) { // 外部迭代
        System.out.println(o);
    }

可以写成:

    list.forEach(o -> {System.out.println(o);}); //forEach函数实现内部迭代

3.2.2) 流

 List<Shape> shapes = ...
    shapes.stream()
      .filter(s -> s.getColor() == BLUE)
      .forEach(s -> s.setColor(RED));

filter方法的参数是Predicate类型,forEach方法的参数是Consumer类型,它们都是函数接口,所以可以使用λ表达式

还有一个方法叫parallelStream(),顾名思义它和stream()一样,只不过指明要并行处理,以期充分利用现代CPU的多核特性。

下面是典型的大数据处理方法,Filter-Map-Reduce:

    //给出一个String类型的数组,找出其中所有不重复的素数
    public void distinctPrimary(String... numbers) {
        List<String> l = Arrays.asList(numbers);
        List<Integer> r = l.stream()
                .map(e -> new Integer(e))
                .filter(e -> Primes.isPrime(e))
                .distinct()
                .collect(Collectors.toList());
        System.out.println("distinctPrimary result is: " + r);
    }

你可能会觉得在这个例子里,List l被迭代了好多次,map,filter,distinct都分别是一次循环,效率会不好。实际并非如此。这些返回另一个Stream的方法都是“懒(lazy)”的,而最后返回最终结果的collect方法则是“急(eager)”的。在遇到eager方法之前,lazy的方法不会执行。

3.2.3) Optional使用

Optional本身用来解决 java臭名昭著的空指针问题,但是借助lamda表达式,做了一些很强大的功能。

1)map

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));
    }
}

该函数的参数定义了一个lamda目标函数,当Optional值为空时,返回空。

不为空值,则将value作为lamda目标函数的参数T,返回值R根据参数实际返回

加入Opational调用如下方法:

map(StreamExecutionEnvironmentFactory::createExecutionEnvironment)

将根据实际值是否为空。  部位空则调用 lamda表达式的方法引用 StreamExecutionEnvironmentFactory.createExecutionEnvironment(value)

4 方法引用

任何一个λ表达式都可以代表某个函数接口的唯一方法的匿名描述符。我们也可以使用某个类的某个具体方法来代表这个描述符,叫做方法引用

/c1 与 c2 是一样的(静态方法引用)
    Comparator<Integer> c2 = (x, y) -> Integer.compare(x, y);
    Comparator<Integer> c1 = Integer::compare;

 //下面两句是一样的(实例方法引用1)
    persons.forEach(e -> System.out.println(e));
    persons.forEach(System.out::println);

//下面两句是一样的(实例方法引用2)
    persons.forEach(person -> person.eat());
    persons.forEach(Person::eat);

 //下面两句是一样的(构造器引用)
    strList.stream().map(s -> new Integer(s));
    strList.stream().map(Integer::new);

参考文档:https://blog.csdn.net/ioriogami/article/details/12782141

发布了16 篇原创文章 · 获赞 0 · 访问量 2816

猜你喜欢

转载自blog.csdn.net/peidezhi/article/details/104049777