读书笔记——《Java 8 实战》系列之方法引用

本贴最后更新于 2368 天前,其中的信息可能已经时异事殊

上一篇博客中,我们继续介绍了一些关于 Lambda 表达式的相关知识。在本篇博客中,我将向大家介绍一种通过调用特定方法来使 Lambda 表达式更简捷的办法——方法引用。事实上,方法引用就是让你根据已有的方法实现来创建 Lambda 表达式。

简单来说,方法引用的语法就是:

目标引用::方法名称

需要注意的是这里的方法名称后面不加圆括号(),因为我们并没有实际调用这个方法

方法引用大概分为三类:

类型方法引用示例
指向静态方法的方法引用Class::staticMethod(如 Integer::parseInt)
指向任意类型实例方法的方法引用Class::instanceMethod(如 String::length)
指向现有对象的实例方法的方法引用object::instanceMethod(如 student::getHeight)

第二种方法引用与第三种方法引用乍一看会让人有些迷惑,实际上第二种方法引用主要是用在当我们 Lambda 表达式的主体中调用参数对象的某个方法时,如:

	(String s) -> s.length()     可以改写成     String::length

而第三种方法引用主要时用在当我们在调用一个外部对象的方法时,如:

	() -> student.getHeight()    可以改写成      student::getHeight

下图是为三种不同类型的 Lambda 表达式构建方法引用的办法

e4ebadd76a1247e5a12993f2fadbef18.png

接下来我就用一个例子——给学生利用不同策略来排序为大家总结一下,行为参数化Lambda 表达式(一)Lambda 表达式(二)方法引用这四篇博客的重要内容。

  • 首先是我们的 Student 实体类
	class Student{
		private String name;           //学生姓名
		private Integer avgScore;          //平均成绩
		private Integer height;            //学生身高

		public Student(String name, Integer avgScore, Integer height) {
			super();
			this.name = name;
			this.avgScore = avgScore;
			this.height = height;
		}

		public String getName() {
			return name;
		}
		public void setName(String name) {
			this.name = name;
		}
		public Integer getAvgScore() {
			return avgScore;
		}
		public void setAvgScore(int avgScore) {
			this.avgScore = avgScore;
		}
		public Integer getHeight() {
			return height;
		}
		public void setHeight(int height) {
			this.height = height;
		}
		@Override
		public String toString() {
			return this.getName();
		}
	}
  • Java 的 API 类库中已经提供了 List 的 sort 方法,我们不用去自己实现它,只需要传入一个 Comparator 类型的对象,list 就可以按照它的规则来排序
	void sort(Comparator<? super E> c)
  • Comparator是一个函数式接口,它提供了一个比较两个对象大小的方法 compare(T o1, T o2),返回一个 int 类型的值,该返回值可能为负数,0,或者正数,分别对应着 o1 小于,等于,或者大于 o2。
	//返回值可能为负数,0,或者正数,分别对应着o1小于,等于,或者大于o2
	int compare(T o1, T o2);
  • 要想将一个 List 中的学生以某种规则排序,我们以行为参数化的角度来考虑,第一种解决方案应该如下:
	//实现一个Comparator接口的实体类StudentHeightComparator,通过比较两个学生的身高,来比较两个student对象的大小
	class StudentHeightComparator implements Comparator<Student>{

	@Override
	public int compare(Student s1, Student s2) {
		// TODO Auto-generated method stub
		return s1.getHeight().compareTo(s2.getHeight());
	}
	
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		List<Student> students = new ArrayList<>();
		students.add(new Student("a",90,180));
		students.add(new Student("b",80,175));
		students.add(new Student("c",70,190));
		
		//实例化StudentHeightComparator,并将其作为参数传入到sort方法中去
		students.sort(new StudentHeightComparator());
		System.out.println(students.toString());
	}
	
}
  • 匿名类的机制可以一定程度上优化我们的第一种解决方案,它的解决方案应该如下:
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		List<Student> students = new ArrayList<>();
		students.add(new Student("a",90,180));
		students.add(new Student("b",80,175));
		students.add(new Student("c",70,190));
		
		//使用匿名类,我们就无需实现一个只需实例化一次的类StudentHeightComparator
		students.sort(new Comparator<Student>() {
			@Override
			public int compare(Student s1, Student s2) {
				// TODO Auto-generated method stub
				return s1.getHeight().compareTo(s2.getHeight());
			}
		});
		System.out.println(students.toString());
	}
  • 尽管匿名类一定程度上减少了方案一中啰嗦的代码,但我们可以使用 Lambda 表达式来使方案二变得更加简单:
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		List<Student> students = new ArrayList<>();
		students.add(new Student("a",90,180));
		students.add(new Student("b",80,175));
		students.add(new Student("c",70,190));
		
		//Comparator实际上代表了(T,T) -> int 的函数描述符
		students.sort((s1,s2) -> s1.getHeight().compareTo(s2.getHeight()));
		System.out.println(students.toString());
	}
  • 现在我们来为实体类 Student 增加一个静态方法,来写出一个不一样的 Lambda 表达式:
	class Student{
		
		......
		
		public static int compareStudentByHeight(Student s1, Student s2) {

			return s1.getHeight().compareTo(s2.getHeight());

		}
	}
	
	public static void main(String[] args) {
		List<Student> students = new ArrayList<>();
		students.add(new Student("a",90,180));
		students.add(new Student("b",80,175));
		students.add(new Student("c",70,190));
		
		//使用Student类的静态方法
		students.sort((s1,s2) -> Student.compareStudentByHeight(s1,s2));
		System.out.println(students.toString());
	}
  • 最后,我们使用方法引用来将上面的代码变得更加简洁,使得代码阅读起来就像问题描述的一样:
	public static void main(String[] args) {
		List<Student> students = new ArrayList<>();
		students.add(new Student("a",90,180));
		students.add(new Student("b",80,175));
		students.add(new Student("c",70,190));
		
		//使用Student类的静态方法的方法引用
		students.sort(Student::compareStudentByHeight);
		System.out.println(students.toString());
	}
  • 最后的这种解决方案呢,其实是用了我们上面提到的指向静态方法的方法引用,那么同学们能不能自己写出剩下的两种类型的方法引用呢?

指向任意类型实例方法的方法引用实例

	class Student{
		......
		
		//一个普通的实例方法
		public int compareToStudentByHeight(Student s) {
			return this.getHeight().compareTo(s.getHeight());
		}
	}
	
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		List<Student> students = new ArrayList<>();
		students.add(new Student("a",90,180));
		students.add(new Student("b",80,175));
		students.add(new Student("c",70,190));
		
		//使用任意类型对象实例方法的Lambda表达式形式,等价于下一行代码
		students.sort((s1,s2) -> s1.compareToStudentByHeight(s2));
		
		//使用指向任意类型对象实例方法的方法引用,进一步简化了上一行代码
		students.sort(Student::compareToStudentByHeight);
		
		System.out.println(students.toString());
	}

指向现有对象的实例方法的方法引用

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		List<Student> students = new ArrayList<>();
		students.add(new Student("a",90,180));
		students.add(new Student("b",80,175));
		students.add(new Student("c",70,190));
		
		StudentComparatorProvider provider = new StudentComparatorProvider();
		
		//使用指向现有对象的实例方法的Lambda表达式形式,等价于下一行代码
		students.sort((s1,s2) -> provider.compareStudentByHeight(s1, s2));
		
		//使用指向现有对象的实例方法的方法引用,进一步简化了上一行代码
		students.sort(provider::compareStudentByHeight);
		
		System.out.println(students.toString());
	}
  • 除此之外,对于一个现有的构造函数,你可以利用它的名称和关键字 new 来创建一个它的引用,首先我们先稍微修改下我们的 Student 实体类,在实体类中分别提供无参,一个参数和两个参数的构造函数。
	class Student{
		private String name;           //学生姓名
		private Integer avgScore;          //平均成绩

		public Student() {
			this.name = "defaultName";
			this.avgScore = 0;
		}

		public Student(String name) {
			this.name = name;
			this.avgScore = 0;
		}

		public Student(String name, Integer avgScore) {
			this.name = name;
			this.avgScore = avgScore;
		}

		public String getName() {
			return name;
		}
		public void setName(String name) {
			this.name = name;
		}
		public Integer getAvgScore() {
			return avgScore;
		}
		public void setAvgScore(int avgScore) {
			this.avgScore = avgScore;
		}
		@Override
		public String toString() {
			return "姓名:"+this.getName()+" 平均分: "+this.getAvgScore();
		}
	
	}
  • 假如使用 new Student()构造函数,它的函数签名是() -> Student, 符合 Supplier 的() -> T 的函数签名,因此我们可以用以下几种不同的方法创建一个 Student 实例:
	public static void main(String[] args) {
		
		//使用无参构造函数创建实例
		Student student1 = new Student();
		System.out.println(student1.toString());
		
		//使用Lambda表达式创造实例,等价于下面使用方法引用创建实例
		Supplier<Student> s1 = () -> new Student();
		Student student2 = s1.get();
		System.out.println(student2.toString());
		
		//使用方法引用进一步简化Lambda表达式的写法
		Supplier<Student> s= Student::new;
		Student student3 = s.get();
		System.out.println(student3.toString());
		
	}
  • 假如使用 new Student(String name)构造函数,它的函数签名是(String) -> Student,符合 Function<T,R> 的(T) -> R 的函数签名,因此我们可以用以下几种不同的方法创建一个 Student 实例:
	public static void main(String[] args) {
		
		//使用一个参数构造函数创建实例
		Student student1 = new Student("Jesmin");
		System.out.println(student1.toString());
		
		//使用Lambda表达式创造实例,等价于下面使用方法引用创建实例
		Function<String,Student> f = s -> new Student(s);
		Student student2 = f.apply("Jesmin");
		System.out.println(student2.toString());
		
		//使用方法引用进一步简化Lambda表达式的写法
		Function<String,Student> f2= Student::new;
		Student student3 = f2.apply("Jesmin");
		System.out.println(student3.toString());
		
	}
  • 假如使用 new Student(String name, Integer avgScore)构造函数,他的函数签名是(String,Integer) -> Student,符合 BiFunction<T,U,R> 的 <T,U> -> R 的函数签名,因此我们可以用以下几种不同的方法创建一个 Student 实例:
	public static void main(String[] args) {
		
		//使用两个参数构造函数创建实例
		Student student1 = new Student("Jesmin",90);
		System.out.println(student1.toString());
		
		//使用Lambda表达式创造实例,等价于下面使用方法引用创建实例
		BiFunction<String,Integer,Student> bf = (s,i) -> new Student(s,i);
		Student student2 = bf.apply("Jesmin",90);
		System.out.println(student2.toString());
		
		//使用方法引用进一步简化Lambda表达式的写法
		BiFunction<String,Integer,Student> bf2= Student::new;
		Student student3 = bf2.apply("Jesmin",90);
		System.out.println(student3.toString());
		
	}
  • B3log

    B3log 是一个开源组织,名字来源于“Bulletin Board Blog”缩写,目标是将独立博客与论坛结合,形成一种新的网络社区体验,详细请看 B3log 构思。目前 B3log 已经开源了多款产品:SymSoloVditor思源笔记

    1083 引用 • 3461 回帖 • 285 关注
  • Java

    Java 是一种可以撰写跨平台应用软件的面向对象的程序设计语言,是由 Sun Microsystems 公司于 1995 年 5 月推出的。Java 技术具有卓越的通用性、高效性、平台移植性和安全性。

    3168 引用 • 8207 回帖
  • 方法引用
    2 引用

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...

推荐标签 标签

  • JSON

    JSON (JavaScript Object Notation)是一种轻量级的数据交换格式。易于人类阅读和编写。同时也易于机器解析和生成。

    51 引用 • 190 回帖
  • Jenkins

    Jenkins 是一套开源的持续集成工具。它提供了非常丰富的插件,让构建、部署、自动化集成项目变得简单易用。

    51 引用 • 37 回帖 • 1 关注
  • 前端

    前端技术一般分为前端设计和前端开发,前端设计可以理解为网站的视觉设计,前端开发则是网站的前台代码实现,包括 HTML、CSS 以及 JavaScript 等。

    247 引用 • 1347 回帖
  • 爬虫

    网络爬虫(Spider、Crawler),是一种按照一定的规则,自动地抓取万维网信息的程序。

    106 引用 • 275 回帖
  • 自由行
  • C

    C 语言是一门通用计算机编程语言,应用广泛。C 语言的设计目标是提供一种能以简易的方式编译、处理低级存储器、产生少量的机器码以及不需要任何运行环境支持便能运行的编程语言。

    83 引用 • 165 回帖 • 43 关注
  • 互联网

    互联网(Internet),又称网际网络,或音译因特网、英特网。互联网始于 1969 年美国的阿帕网,是网络与网络之间所串连成的庞大网络,这些网络以一组通用的协议相连,形成逻辑上的单一巨大国际网络。

    96 引用 • 330 回帖 • 1 关注
  • 微信

    腾讯公司 2011 年 1 月 21 日推出的一款手机通讯软件。用户可以通过摇一摇、搜索号码、扫描二维码等添加好友和关注公众平台,同时可以将自己看到的精彩内容分享到微信朋友圈。

    129 引用 • 793 回帖
  • 游戏

    沉迷游戏伤身,强撸灰飞烟灭。

    169 引用 • 799 回帖
  • Node.js

    Node.js 是一个基于 Chrome JavaScript 运行时建立的平台, 用于方便地搭建响应速度快、易于扩展的网络应用。Node.js 使用事件驱动, 非阻塞 I/O 模型而得以轻量和高效。

    138 引用 • 268 回帖 • 197 关注
  • SMTP

    SMTP(Simple Mail Transfer Protocol)即简单邮件传输协议,它是一组用于由源地址到目的地址传送邮件的规则,由它来控制信件的中转方式。SMTP 协议属于 TCP/IP 协议簇,它帮助每台计算机在发送或中转信件时找到下一个目的地。

    4 引用 • 18 回帖 • 587 关注
  • 房星科技

    房星网,我们不和没有钱的程序员谈理想,我们要让程序员又有理想又有钱。我们有雄厚的房地产行业线下资源,遍布昆明全城的 100 家门店、四千地产经纪人是我们坚实的后盾。

    6 引用 • 141 回帖 • 559 关注
  • wolai

    我来 wolai:不仅仅是未来的云端笔记!

    1 引用 • 11 回帖 • 2 关注
  • iOS

    iOS 是由苹果公司开发的移动操作系统,最早于 2007 年 1 月 9 日的 Macworld 大会上公布这个系统,最初是设计给 iPhone 使用的,后来陆续套用到 iPod touch、iPad 以及 Apple TV 等产品上。iOS 与苹果的 Mac OS X 操作系统一样,属于类 Unix 的商业操作系统。

    84 引用 • 139 回帖
  • 倾城之链
    23 引用 • 66 回帖 • 100 关注
  • 知乎

    知乎是网络问答社区,连接各行各业的用户。用户分享着彼此的知识、经验和见解,为中文互联网源源不断地提供多种多样的信息。

    10 引用 • 66 回帖
  • frp

    frp 是一个可用于内网穿透的高性能的反向代理应用,支持 TCP、UDP、 HTTP 和 HTTPS 协议。

    15 引用 • 7 回帖 • 11 关注
  • 智能合约

    智能合约(Smart contract)是一种旨在以信息化方式传播、验证或执行合同的计算机协议。智能合约允许在没有第三方的情况下进行可信交易,这些交易可追踪且不可逆转。智能合约概念于 1994 年由 Nick Szabo 首次提出。

    1 引用 • 11 回帖 • 7 关注
  • 安装

    你若安好,便是晴天。

    128 引用 • 1184 回帖
  • 区块链

    区块链是分布式数据存储、点对点传输、共识机制、加密算法等计算机技术的新型应用模式。所谓共识机制是区块链系统中实现不同节点之间建立信任、获取权益的数学算法 。

    91 引用 • 751 回帖 • 1 关注
  • AngularJS

    AngularJS 诞生于 2009 年,由 Misko Hevery 等人创建,后为 Google 所收购。是一款优秀的前端 JS 框架,已经被用于 Google 的多款产品当中。AngularJS 有着诸多特性,最为核心的是:MVC、模块化、自动化双向数据绑定、语义化标签、依赖注入等。2.0 版本后已经改名为 Angular。

    12 引用 • 50 回帖 • 424 关注
  • OpenShift

    红帽提供的 PaaS 云,支持多种编程语言,为开发人员提供了更为灵活的框架、存储选择。

    14 引用 • 20 回帖 • 602 关注
  • 服务

    提供一个服务绝不仅仅是简单的把硬件和软件累加在一起,它包括了服务的可靠性、服务的标准化、以及对服务的监控、维护、技术支持等。

    41 引用 • 24 回帖 • 4 关注
  • FFmpeg

    FFmpeg 是一套可以用来记录、转换数字音频、视频,并能将其转化为流的开源计算机程序。

    22 引用 • 31 回帖 • 1 关注
  • 尊园地产

    昆明尊园房地产经纪有限公司,即:Kunming Zunyuan Property Agency Company Limited(简称“尊园地产”)于 2007 年 6 月开始筹备,2007 年 8 月 18 日正式成立,注册资本 200 万元,公司性质为股份经纪有限公司,主营业务为:代租、代售、代办产权过户、办理银行按揭、担保、抵押、评估等。

    1 引用 • 22 回帖 • 685 关注
  • RYMCU

    RYMCU 致力于打造一个即严谨又活泼、专业又不失有趣,为数百万人服务的开源嵌入式知识学习交流平台。

    4 引用 • 6 回帖 • 39 关注
  • 黑曜石

    黑曜石是一款强大的知识库工具,支持本地 Markdown 文件编辑,支持双向链接和关系图。

    A second brain, for you, forever.

    10 引用 • 85 回帖 • 1 关注