方法引用:

之前花了很多时间对Lambda表达式进行了深入的学习,接下来开启新的主题---方法引用(Method References),其实在之前的学习中已经使用过了,如:

那方法引用跟Lambda表达式是一种什么关系呢?其实可以理解为它是Lambda表达式的一个语法糖(Syntactic sugar),什么是语法糖呢,可以看一下百科:

在很多时候在能使用Lambda表达式实现的功能,恰巧该功能刚好有一个方法能表达出来,那么就可以通过方法引用的方式来替换掉Lambda表达式使得代码更加精简,更加漂亮。当然方法引用是有局限性的,并非是一种非常通用的结构,为啥,因为很多时候Lambda表达式并非能用方法引用来替换,那换句话:什么时候Lambda表达式能用方法引用替换呢?主要看Lambda表达式的方法体里面的实现,如果恰好方法体里面的实现刚好有一个方法能提供该功能那就可以采用方法引用的方式替换,相反如果方法体里面的功能比较复杂并未有现成的方法来提供那就不能替换。

空谈了这么多,没有代码的体现当然跟听天书一样,下面开始撸码,先从集合元素的打印开启方法引用的探索:

没啥可解释的,接着可以变换方法引用的样式,如下:

很显然使用过方法引用的方式代码更加简洁,当然乍一看也是更加难以读懂滴,当然系统掌握了方法引用了之后就个难懂就会变为习以为常滴啦,接着继续,其中当鼠标点击"::"时,IDE会自动跳到函数式接口里,这里就不演示了,也就是确实是Lamdba表达式的另一种表现形式,接着来仔细观察一下这种方法引用的写法:

System.out是代表什么呢,点进去看一下:

而"::"的右侧的println,则是PrintStream类中的println()实例方法:

这是方法引用的一种表现形式,当然之后会详细介绍方法引用的所有方式,这是后话,这里先来针对这个例子讨论一下方法引用,其实我们可以将方法引用看作是一个函数指针【function pointer】,函数指针不是c、c++的概念么,当然这里是可以理解成函数指针啦,怎么理解:

接下来由这个示例做为引子来对方法引用做一个详细的介绍,在上面也说了方法引用是有多种表现形式的,其实方法引用是有4类,下面则一一进行详细学习。

1、类名::静态方法名

这里就直接上代码进行学习,以对学生进行排序为场景,先新建学生的实体:

因为要根据名字与成绩进行排序,所以将其排序的方法直接定义在Student实体中,而又因为当前探讨的方法引用形式是"类名::静态方法名",所以将这两个排序方法也定义为static的【先不用管这么写代码的合理性,主要是为了方便说明问题】:

public class Student {
private String name;
private int score; public Student(String name, int score) {
this.name = name;
this.score = score;
} public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} public int getScore() {
return score;
} public void setScore(int score) {
this.score = score;
} /**
* 根据学生成绩进行升序排序
*/
public static int comparedStudentByScore(Student student1, Student student2) {
return student1.getScore() - student2.getScore();
} /**
* 根据学生名字的ASCII码进行升序排序
*/
public static int comparedStudentByName(Student student1, Student student2) {
return student1.getName().compareToIgnoreCase(student2.getName());
}
}

接着初始化一个集合:

然后对集合元素进行排序,传统的作法是:

但是在Java1.8中List中直接增加了排序的方法如下:

查看一下该方法的源码:

所以这里用Lambda表达式用这种方法来排序,如下:

上面这种写法已经比较熟了,接着引入主题,采用方法引用的方式来进行排序,在写代码之前先观察一下这个排序:

满足这个特性的代码刚好就可以用方法引用来改写,如下:

很显然第一种方法引用的方式就出现了:

细细体会一下:

所以进一步说明方法引用就是Lambda表达式的一种语法糖,功能一样,只是代码看起来更加简洁了,其实对于方法引用的使用是有一个要求的:你所使用的Lambda表达式的方法体恰好有一个匹配的方法完成相同的功能,这时候就可以将Lambda表达式替换成方法引用,除此之外是无法用方法引用替换Lambda表达式的,所以说Lambda表达式是一种更为通用的方式,而方法引用是需要满足一定的条件才能够使用的。

接着根据用户名来进行排序,so easy啦:

【注意】:这上"::"这是在Java8中新增的一种语法形式,它的左边是什么和右边是什么方法都得取决于上下文。

2、引用名【对象名】::实例方法名

这里为了说明这种类型,新建一个类,然后将比较器由之前的静态方法声明为实例方法:

下面使用这个新的比较器对象来同样对元素进行排序,还是先用Lambda表达式来实现:

接着使用新的方法引用的形式来替换:

同样的如果想根据名字来进行排序:

3、类名::实例方法名【最难理解】

如小标题所示,这种方法引用的方式是所有方式中最维理解的,为什么呢?通常如果用类名去调用方法那这方法必然是静态的,然而这里居然用类名直接可以跟上实例方法名,这不逆天了么?其实产生这种疑惑的主要根源在于将方法引用联想成了方法调用,这两个是完全不一样的概念,所以不要混为一谈啦。另外这种方式跟上面的两种方式在参数的传递上是有一些不同的, 下面通过例子来看一下:

先来看一下咱们在Student中定义的两个静态比较方法:

那接下来修改代码来弥补这种设计上的缺失:

接下来调用一下新设计的方法:

接下来理解一下这种写法,有些奇怪,sort()方法接收的是一个ComParator接口,而它是需要接收两个参数的,而看一下咱们目前传递的:

那对应不上了呀,其实理解这种写法核心在于:

这个方法一定是由sort()方法接受的Lambda表达式第一个参数来调用的,而如果Lambda表达式有多个参数,则除了第一个参数之外的所有参数都将作为方法的参数传递进去,还是有些抽象,这里用代码进一步理解:

哇~~好隐晦的,所以说这种方法引用是最难理解的,不过理解了之后再看到这样的写法就比较容易懂了,那接下来对名字再进行排序雷同的写法啦:

接下来再来举个新的例子,对城市列表名进行排序,先用传统的方式,代码如:

接着改用方法引用的方式,由于String中的compareToIgnoreCase()方法正好符合第三种方法引用的要求,如下:

所以说改用方法引用的写法如下:

当然对于打印这块的语句也能改成方法引用,如下:

先看一下out是什么东东:

而println()是PrintStream中的实例方法:

所以这是咱们学的第二种方法引用:对象名::实例方法名。

4、构造方法引用,其表现形式为:类名::new

这是最后一个方法引用的形式,学完那方法引用就可以搞通啦,加油~~下面看代码:

其Supplier的函数原型这里回顾一下:

接着来调用一下它,先想一下,对于String的构造方法是不是刚好满足Supplier的函数要求:不接收参数返回一个String,所以可以直接用方法引用的方式:

编译运行:

再回过头来用鼠标点一下方法引用会发现:

可见IDE直接就识别了是调用了String的无参的构造方法。这就是构造方法引用的使用方式,接下来继续:

接着调用一下它:

依然可以用这种引用方式,为什么,这时点鼠标看IDE识别到了哪个方法:

很显然定位到了带参数的String构造函数去了,而它正好符合Function函数的要求:接收一个参数,返回一个参数。

以上就是涉及到方法引用的所有形式,需要好好消化。

默认方法【default method】:

关于默认方法在之前的学习中也反复提及到了,这里再集中学习一下它,默认方法的定义应该不用过多解释了,就是在接口中有具体实现的方法必须要带上default关键字,而这样的方法就称之为默认方法,下面咱们自己定义一个接口:

然后再新建一个类直接实现该接口:

比较好理解,接着再定义一个接口:

那问题来了,如果MyClass同时实现这两个接口会有什么情况?最终打印的myMethod会是哪个接口的呢?下面试试就知道了:

对于这个错误其实也能理解:因为myMethod同时存在于MyInterface1、MyInterface2接口当中,那对于MyClass而言当然不知道要继承哪一个接口的方法,所以这时要解决这个错误就需要在MyClass中重写myMethod方法,如下:

那如果就想执行它继承的某个接口的myMethod()方法呢?可以这样处理:

接下来再来折腾,新建一个实现类来实现MyInterface1,如下:

接下来让MyClass类继承它,并实现MyInterface2,如下:

可见编译器没有报错,那请问这时打印的结果是?

为什么?这其实是Java的一个约定:实现类的优先级要比接口的优先级要更高一些,如下:

java8学习之方法引用详解及默认方法分析的更多相关文章

  1. 2020你还不会Java8新特性?方法引用详解及Stream 流介绍和操作方式详解(三)

    方法引用详解 方法引用: method reference 方法引用实际上是Lambda表达式的一种语法糖 我们可以将方法引用看作是一个「函数指针」,function pointer 方法引用共分为4 ...

  2. Lambda语言篇 —— lambda, 方法引用, 目标类型和默认方法

    本文介绍了Java SE 8中新引入的lambda语言特性以及这些特性背后的设计思想.这些特性包括: lambda表达式(又被成为"闭包"或"匿名方法") 方法 ...

  3. java8学习之Optional深入详解

    自上次[http://www.cnblogs.com/webor2006/p/8243874.html]函数式接口的学习告一段落之后,这次来学习一下Optional,它并非是函数式接口的概念,点击查看 ...

  4. 解决ajax跨域的方法原理详解之Cors方法

    1.神马是跨域(Cross Domain)   对于端口和协议的不同,只能通过后台来解决.   一句话:同一个ip.同一个网络协议.同一个端口,三者都满足就是同一个域,否则就是 跨域问题了.而为什么开 ...

  5. Java基础学习总结(33)——Java8 十大新特性详解

    Java8 十大新特性详解 本教程将Java8的新特新逐一列出,并将使用简单的代码示例来指导你如何使用默认接口方法,lambda表达式,方法引用以及多重Annotation,之后你将会学到最新的API ...

  6. Asp.Net MVC学习总结之过滤器详解(转载)

    来源:http://www.php.cn/csharp-article-359736.html   一.过滤器简介 1.1.理解什么是过滤器 1.过滤器(Filters)就是向请求处理管道中注入额外的 ...

  7. C#操作SQLite方法实例详解

    用 C# 访问 SQLite 入门(1) CC++C#SQLiteFirefox  用 C# 访问 SQLite 入门 (1) SQLite 在 VS C# 环境下的开发,网上已经有很多教程.我也是从 ...

  8. php中关于引用(&)详解

    php中关于引用(&)详解 php的引用(就是在变量或者函数.对象等前面加上&符号) 在PHP 中引用的意思是:不同的变量名访问同一个变量内容. 与C语言中的指针是有差别的.C语言中的 ...

  9. PHP开发中常见的安全问题详解和解决方法(如Sql注入、CSRF、Xss、CC等

    页面导航: 首页 → 网络编程 → PHP编程 → php技巧 → 正文内容 PHP安全 PHP开发中常见的安全问题详解和解决方法(如Sql注入.CSRF.Xss.CC等) 作者: 字体:[增加 减小 ...

随机推荐

  1. LeetCode.1185-一周中的星期几(Day of the Week)

    这是小川的第415次更新,第448篇原创 看题和准备 今天介绍的是LeetCode算法题中Easy级别的第266题(顺位题号是1185).给定日期,返回该日期的星期几.输入为三个整数,分别代表日,月和 ...

  2. ImportError: libpython3.6m.so.1.0: cannot open shared object file: No such file or directory

    该错误原因是libpython3.6m.so.1.0不存在 解决方案 1.查看/usr/lib/x86_64-linux-gnu/目录下是否存在libpython3.m.so.1.0文件,或者直接全盘 ...

  3. 【VS开发】组播(多播)的C程序实战

    每个人都有不同的认知规律和习惯, 有的人喜欢搞一套严密的大理论, 论述起来滔滔不绝, 不管自己懂不懂, 反正读者/听者是没搞懂. 有的人喜欢从实践出发, 没看到代码, 不运行一下, 不看到结果, 就不 ...

  4. 【VS开发】ATL辅助COM组件开发

    有些时候在程序的编写过程中我们会跨语言写一些东西,比如在C#中使用到C++,这个时候COM的出现就很好的解决了这一问题,我们如何来创建并且编写COM组件呢? 一.首先:创建一个ATL项目,如下图所示: ...

  5. #import "msado15.dll" no_namespace rename("EOF","adoEOF")

    引入动态链接库msado15.dll的信息, no_namespace--无命名空间 rename(“EOF”,“adoEOF”)--把文件结束符“EOF”,更换成“adoEOF”

  6. PAT A1036 Boys vs Girls(25)

    AC代码 #include <cstdio> #include <algorithm> using namespace std; const int max_n = 11000 ...

  7. # Clion中编译多个cpp(实现单文件编译)

    Clion中编译多个cpp(实现单文件编译) 在不做任何配置情况下,Clion工程下只能有一个main()函数,新建多个cpp会导致报main()函数重复定义的错误,所以默认情况下无法在一个工程下编译 ...

  8. django时区与时间差的问题

    时区的正确配置方式: # 这里还可以配置成中文 一般用不到 LANGUAGE_CODE = 'en-us' # TIME_ZONE = 'UTC' TIME_ZONE = 'Asia/Shanghai ...

  9. Python面试常考点之深入浅出链表操作

    Python面试常考点之深入浅出链表操作 在Python开发的面试中,我们经常会遇到关于链表操作的问题.链表作为一个非常经典的无序列表结构,也是一个开发工程师必须掌握的数据结构之一.在本文中,我将针对 ...

  10. Dev-C++的一些使用技巧快捷键

    最近开始用Dev-C++在Window下编程,感觉Dec-C++是一款挺不错的C++编译器.下载地址http://www.duote.com/soft/9830.html .现总结一些使用技巧: 1. ...