java8 中Lambda表达式

一. lambda表达式的使用:

在 c/c++ 中可以使用函数指针的形式,把一个函数(一段代码块)传给另一个函数。而在 c# 中可以使用委托的形式也可以实现

delegate int methodName (int a);
public int sum(int a){
    return a + 4;
}
// 只要返回值 和 参数类型一致那么就能把方法委托给methodName
methodName=sum;

/* 只要这个方法的返回值和委托返回值一样,就可以使用委托 */
public void print(int x){
    print(x);
}
//使用委托
main(){
    //把方法传入进去了。
    print(methodName(4));   // 4 + 4 = 8;
}

这种把方法(行为)传入另一个方法的形式,可以使得程序在功能的实现更加简单,因此在 java8 中也引入了一种方法来支持。

在 java 中使用的是函数式编程接口的方式来实现的:

// 一个函数式编程接口
@FunctionalInterface
public interface Comp<User> {
    boolean test(User user);
}
// 一个方法 返回值 和 参数类型 都和上面接口中的方法一样。
public boolean compUser(User user){
    return user.age > 18    
}

// 使用函数式接口
public void isUser(User user,Comp<User> comp){
    if(comp.test(user)){
        System.out.print("zzzzzz");
    }
}

main(){
    User user = new User(27);
    isUser(user, 这里怎么填呢?); //问题出现了,这里怎么填呢?
}

先忽略那个怎么填的问题?来说一下上面代码的用处。
首先编写一个 函数式编程接口 这个接口有一个特点,接口中只能有一个抽象方法,且加了一个 @FunctionalInterface 注解,实际上,任何一个只有一个抽象方法的接口,没加这个注解,也默认加了。
这个注解中有一个方法:

boolean  test(User user);

然后,自己写一个方法

// 一个方法 返回值 和 参数类型 都和上面接口中的方法一样。
public boolean compUser(User user){
    return user.age > 18    
}

到这里,都和c# 的实现差不多;但出现了一个问题
c# 中可以用 :

// 只要返回值 和 参数类型一致那么就能把方法委托给methodName,methodName变成一个方法引用
methodName=sum;
//得到委托,然后把调用的时候只要调用的方法的参数值和委托的返回值一样,那么就可以把委托传进去。

而 java 有着 万物皆是对象 的美称,因此它的处理方式是,只要调用的方法的参数值是一个 函数式接口 ,那么就能把和接口实现方法的 返回值 和 参数值 一样的方法传入,如上面的 compUser(User user)
回到上面如何传值的问题上来,怎么把这个方法传进来呢?java中并没有委托,但是有两种其他的方式:

  1. 匿名函数,Lambda 表达式
  2. 方法引用

1.1 Lambda 匿名函数

匿名函数的写法就是:
(参数列表) -> { 方法体 }
所以

public boolean compUser(User user){
    return user.age > 18    
}

等于

(User user) -> { return user.age > 18 }

具体关于Lambda表达式的详细,可以自行百度。

那么我们来回答怎么填的问题:

main(){
    User user = new User(27);
    isUser(user, (User user) -> { return user.age > 18 }); 
}

1.2 方法引用

在使用Lambda表达式中,虽然大多时候,为了简单,自己写一个lambda表达式如:

e -> e.getName();   // 传入e,获取e的名字

但很多时候,需要使用的方法,已经存在某个类中了,那其实就不用再自己写lambda表达式了,通过方法引用 :: 来把那个方法包装成函数式接口的形式,作为参数传递。

扫描二维码关注公众号,回复: 1649565 查看本文章

java中有这4种写法:

类型 示例
引用静态方法 ContainingClass::staticMethodName String::valueOf
引用某个对象的实例方法 containingObject::instanceMethodName str::toString
引用某个类型的任意对象的实例方法 ContainingType::methodName String::length
引用构造方法 ClassName::new String::new

下面来通过一个类来详细说一下这4种的用途:

public class User {
    private Integer id;
    private String name;

    public static String getName3(User user){
        return user.getName();
    }

    public  String getName2(User user){
        return user.getName();
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}
  • 引用静态方法:
    ClassName::staticMethodName
list.Stream.map(User::getName3)  //这相当于class.staticMethod 很好理解。
  • 引用实例方法
    instance::methodName
list.Stream.map(user::getName2)  // 这相当于 instance.methodName ,也相当于 user -> user.getName2
  • 通过Class调用非静态方法
    ClassName:: methodName
/*
这种最不好理解了,怎么可以通过类来调用非静态方法呢?
看前面的代码User 中的 getName 其中geName是没有参数的。
但map(T accept(R r)) 其中函数式接口是有参数的。

这就是 类名::实例方法名 这种方法引用的特殊之处了:
当使用 类名::实例方法名 方法引用时,一定是lambda表达式所接收的第一个参数来调用实例方法,如果lambda表达式接收多个参数,其余的参数作为方法的参数传递进去。
即:类作为调用方,会作为一个实际特殊的参数。
*/
list.Stream.map(User::getName)
list.stream.map(User::getName2) //这种写法就是错误的。因为getName2自带参数。

public String getName(){
    return this.name;
}
  • 类名:: new
    这个就没什么好说的啦

重点关注: 类名: : 实例方法, 会把调用方作为参数传入。

1.3 java 中的4种函数接口

     /**
     * Java8内置的四大核心函数式接口
     * <p>
     * Consumer<T>: 消费型接口
     * void accept(T t);
     *
     * Supplier<T>:供给型接口
     * T get();
     *
     * Function<T, R>: 函数型接口
     * R apply(T t);
     *
     * Predicate<T>: 断言型接口:
     * boolean test(T t);
     *
     *Java8中还提供了其他函数式接口
     */

java中由于泛型的的存在,因此java中内置了4种通用的函数接口,来满足大部分方法。

猜你喜欢

转载自blog.csdn.net/mooneal/article/details/78471351
今日推荐