Java面向对象_22 函数式编程、Lambda、方法引用


在之前的匿名内部类以及“使猪飞”的例子都是函数式编程,这篇文章将详细介绍什么是函数式编程、lambda表达式。

这是本文章代码使用的例子:
这是一个名为User的实体类,有构造器,重写了toString(),以及run()和say()方法用于举例。

public class User {
    private String name;
    private int age;

    public User() {
    }

    public User(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }
    
    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

    public void run(Runnable r){
        r.run();
    }

    public void say(String message){
        System.out.println(message);
    }

    public static String address(){
        return "China";
    }
}

定义两个接口

public interface MessageInterface {
    public String sayMessage();
}
public interface Runnable {
    public void run();
}

函数式编程

我们的面向对象编程太过于强调“必须通过对象的形式来做事情”,尤其Java,完全依赖于对象。而函数式思想可以忽略面向对象的复杂语法,函数式编程就是强调做什么,而不是强调哪个对象去怎么做,这也是面向函数和面向对象的区别。

以下就是函数替代对象的写法(也就是使用匿名内部类):

        //Case1:
        //The method {User.run()} that takes an object that
        // implements the Runnable interface.
        //When we use functional programming, we simply implement
        // unimplemented method{Runnable.run()} in {new Runnable()},
        //you don't have to create a object.
        new User().run(new Runnable() {
            @Override
            public void run() {
                System.out.println("The user is running!");
            }
        });
        //Case2:
        //You can also use the interface method in functional programming.
        System.out.println(new MessageInterface() {
            @Override
            public String sayMessage() {
                return "Hello, are you OK?";
            }
        }.sayMessage());

在这里插入图片描述
可见,我们使用匿名内部类的目的不是为了创建对象,而是去做这件事才去创建的。

Lambda表达式

匿名内部类实现起来虽然简单,但是其语法比较冗余。

JDK1.8引入的Lambda表达式,也就是说Lambda表达式是Java8的新特性之一。这种方式只针对有一个抽象方法的接口实现,以简洁的表达式形式实现接口功能来作为方法参数。

lambda的使用

	(参数列表) -> {方法体}

上面的代码,改写为lambda表达式,只需要下面这一行:

	new User().run(() -> {System.out.println("The user is running!");});
	MessageInterface msg = () -> {return "Hello, are you OK?";};
	System.out.println(msg.sayMessage());

是不是简洁很多?但这还不是最简洁的:

如果Lambda表达式的方法体只有一行代码,可以省略大括号不写,同时省略分号;
如果方法体中只有一行return语句,若省略大括号,必须省略return、分号;
参数类型可以省略不写,只有一个参数时()可以省略。

	//省略{},省略;
	new User().run(() -> System.out.println("The user is running!"));
	//省略{},省略return,省略;
	MessageInterface msg = () -> "Hello, are you OK?";
	System.out.println(msg.sayMessage());

推荐使用这种方式,因为写法最简单,看起来最整洁。
当然,即使不会lambda表达式也不会有太大的影响,但是会这个你能看懂别人的的代码,也能让你的代码更优雅!

函数式接口

接口中有且只有一个抽象方法时才能使用Lambda表达式代替匿名内部类,这是因为Lambda表达式是基于函数式接口实现的。函数式接口指的是有且仅有一个抽象方法的接口。

在Java8中,接口上有@FunctionalInterface注解的即为函数式接口,在函数式接口内部只有一个抽象方法。@FunctionalInterface注解只是显示的标注了接口是一个函数式接口,并强制编辑器进行更严格的检查,确保该接口是函数式接口,这与Override注解性质是一样的。

这些是JDK1.8之前已有的一些函数式接口:

java.lang.Runnable
java.util.concurrent.Callable
java.security.PrivilegedAction
java.util.Comparator
java.io.FileFilter
java.nio.file.PathMatcher
java.lang.reflect.InvocationHandler
java.beans.PropertyChangeListener
java.awt.event.ActionListener
javax.swing.event.ChangeListener

JDK1.8新增的函数接口:
java.util.function包下包含了很多类,用来支持Java的函数式编程。

Lambda应用举例

下面介绍一些可用lambda表达式改写的且特别实用的例子。

创建一个集合,同时添加若干对象:

        ArrayList<User> users = new ArrayList<>();
        users.add(new User("Forward Seen",20));
        users.add(new User("Linda",14));
        users.add(new User("Bob",35));
        users.add(new User("Jack",34));
        users.add(new User("Smith",20));

例1:实现集合遍历

遍历输出users集合中的全部User对象:

        //通过匿名内部类的方式遍历
        users.forEach(new Consumer<User>() {
            @Override
            public void accept(User user) {
                System.out.println(user);
            }
        });
        //使用lambda
        users.forEach(user -> System.out.println(user));

因为只有一个参数user,所以( )可以省略
在这里插入图片描述
这一看是用lambda是不是特别简洁,一行代码搞定!

例2:实现集合元素排序

将users中的User对象按照年龄大小降序排序。

        //通过匿名内部类的方式排序
        users.sort(new Comparator<User>() {
            @Override
            public int compare(User o1, User o2) {
                return o2.getAge() - o1.getAge();
            }
        });
		//测试结果
        users.forEach(user -> System.out.println(user));
        //使用lambda
        users.sort((o1, o2) -> o2.getAge() - o1.getAge());

在这里插入图片描述

通过这些例子的使用,我们可以了解,使用lambda可以不用考虑代码实现的过程,我们只需要结果!
换种方式跟你说,我们不需要记住诸如foreach、sort使用时所需要的是什么接口的什么方法,记不住没关系,我们只要知道这种格式,通过lambda表达式一样可以实现我们所要的结果!

方法引用

lambda表达式的主体只有一条语句时,可以通过“::”的格式来引用方法和构造方法。方法引用可以进一步简化lambda表达式的书写,其本质都是对lambda表达式的主体部分已存在的方法进行直接引用。

类名引用静态方法

通过类名对静态方法的引用。

	//Lambda
	MessageInterface msg = () -> User.address();
	System.out.println(msg.sayMessage());
	//静态方法引入
	MessageInterface msg = User::address;
	System.out.println(msg.sayMessage());

对象名引用方法

通过实例化对象的名称来对其方法进行引用。

        User user = new User("Forward", 30);
        //lambda表达式
        MessageInterface msg = () -> user.getName();
        System.out.println(msg.sayMessage());        
        //实例方法引入
        //对象名::方法名,注意没有()
        MessageInterface msg = user::getName; 
        System.out.println(msg.sayMessage());

构造方法引用

对类自带的构造器的引用。

//使用ambda表达式方式
printName("张三", name -> new User(name));
//使用构造器引用的方式
printName("张三", User::new);

类名引用普通方法

通过一个普通类的类名对其普通方法的引用。

	//使用Lambda表达式方式
	printUpper(new StringUtils(),"Hello",(object, t) -> object.printUpperCase());
	//使用方法引用的方式
	printUpper(new StringUtils(),"Hello" ,StringUtils::printUpperCase);
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值