java函数式编程基础(二)

3、lambda表达式

lambda表达式可以说是函数式编程的核心。lambda表达式即匿名函数,它是一段没有函数名的函数体,可以作为参数直接传递给相关的调用者。lambda表达式极大地增强了Java语言的表达能力。
下例展示了lambda表达式的使用,在forEach()函数中,传入的就是一个lambda表达式,它完成了对元素的标准输出操作。可以看到这段表达式并不像函数一样有名字,非常类似匿名内部类,它只是简单地描述了应该执行的代码段。

  List<Integer> list = Arrays.asList(1,2,3,4,5);
  list.forEach((Integer value) -> System.out.println(value));

和匿名对象一样,lambda表达式也可以访问外部的局部变量,如下所示:

final int num =2;
Function<Integer,Integer> stringConvert = (from)-> from * num;
System.out.println(stringConvert.apply(3));

上述代码可以编译通过,正常执行,并输出6。与匿名内部对象一样,在这种情况下,外部的num变量必须申明为final,这样才能保证在lambda表达式中合法的访问它。 但奇妙的是,对于lambda表达式而言,即使去掉上述的final定义,程序依然可以编译通过!但千万不要以为这样你就可以修改num的值了。实际上,这只是Java 8做了一个掩人耳目的小处理,它会自动地将在lambda表达式中使用的变量视为final。因此,下述代码是可以编译通过的:

int num =2;
Function<Integer,Integer> stringConvert = (from)-> from * num;
System.out.println(stringConvert.apply(3));

但是如下面代码执行就会出错:

int num =2;
Function<Integer,Integer> stringConvert = (from)-> {
    from * num;
    num ++;
}
System.out.println(stringConvert.apply(3));

上述的num++会引起一个编译错误: Local variable num defined in an enclosing scope must be final or effectively final

4、方法引用

方法引用是Java 8中提出的用来简化lambda表达式的一种手段。它通过类名和方法名来定位到一个静态方法或者实例方法。 方法引用在Java 8中的使用非常灵活。总的来说,可以分为以下几种:
静态方法引用:ClassName::methodName
实例上的实例方法引用:instanceReference::methodName
超类上的实例方法引用:super::methodName
类型上的实例方法引用:ClassName::methodName
构造方法引用:Class::new
数组构造方法引用:TypeName[]::new
首先,方法引用使用“::”定义,“::”的前半部分表示类名或者实例名,后半部分表示方法名称。如果是构造函数,则使用new表示。

public static void main(String[] args) {
        List<User> users = new ArrayList<>();
        for(int i = 0;i < 10;i++){
            users.add(new User(i,"pony"+i));
        }
        users.stream().map(User::getUserName).forEach(System.out::println);
 }

对于第1个方法引用“User::getName”,表示User类的实例方法。在执行时,Java会自动识别流中的元素(这里指User实例)是作为调用目标还是调用方法的参数。在“User::getName”中,显然流内的元素都应该作为调用目标,因此实际上,在这里调用了每一个User对象实例的getName()方法,并将这些User的name作为一个新的流。同时,对于这里得到的所有name,使用方法引用System.out::println进行处理。这里的System.out为PrintStream对象实例,因此,这里表示System.out实例的println方法,系统也会自动判断,流内的元素此时应该作为方法的参数传入,而不是调用目标。
一般来说,如果使用的是静态方法,或者调用目标明确,那么流内的元素会自动作为参数使用。如果函数引用表示实例方法,并且不存在调用目标,那么流内元素就会自动作为调用目标。
因此,如果一个类中存在同名的实例方法和静态函数,那么编译器就会感到很困惑,因为此时,它不知道应该使用哪个方法进行调用。它既可以选择同名的实例方法,将流内元素作为调用目标,也可以使用静态方法,将流元素作为参数。

public class BadMethodRef {
    public static void main(String[] args) {
        List<Double> numbers = new ArrayList<>();
        for(int i =0;i<10;i++){
            numbers.add(Double.valueOf(i));
        }
        numbers.stream().map(Double::toString).forEach(System.out::println);
    }
}

上述代码试图将所有的Double元素转为String并将其输出,但是很不幸,在Double中同时存在以下两个函数: public static String toString(double d) public String toString() 此时,对函数引用的处理就出现了歧义,因此,这段代码在编译时就会抛出如下错误: Ambiguous method reference: both toString() and toString(double) from the type Double are eligible
方法引用也可以直接使用构造函数。首先,查看模型类User的定义:

public class User implements Serializable {
    private static final long serialVersionUID = -6273531368806894603L;

    private Integer id;

    private String userName;

    public User(Integer id, String userName) {
        this.id = id;
        this.userName = userName;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }
}

下面的方法引用调用了User的构造函数:

public class ConstrMethodRef {
    public static void main(String[] args) {
        List<User> users = new ArrayList<>();
        for(int i =0;i<10;i++){
            users.add(uf.create(i,"pony"+i));
        }
        users.stream().map(User::getUserName).forEach(System.out::println);
    }
    @FunctionalInterface
    interface UserFactory<U extends User> {
        U create(int i,String name);
    }
    static UserFactory<User> uf = User::new;
}

在此,UserFactory作为User的工厂类,是一个函数式接口。当使用User::new创建接口实例时,系统会根据UserFactory.create()的函数签名来选择合适的User构造函数,在这里,很显然就是public User(int id,String name)。在创建UserFactory实例后,对UserFactory.create()的调用,都会委托给User的实际构造函数进行,从而创建User对象实例。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值