持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第3天,点击查看活动详情
今天的问题聚焦在内部类对象,由于内部类的使用一般都要创建对象,那么内部类对象的使用频率显然比内部类高。
内部类对象主要有两个,一个是匿名内部类,一个是lambda表达式,在java8之前,匿名函数一直被用来进行事件回调和监听器,但是自从有了lambda表达式后,大多数人会采用lambda表达式的写法,因为他的代码更简洁。我们先聊一聊匿名内部类的几个要点。
匿名内部类
匿名内部类的语法格式为:
new 要实现的类名/接口名(){
//匿名子类的类体
}
要注意的是:
-
匿名内部类本质上是一个局部内部类对象,所以遵守局部内部类的规则,比如必须定义在局部位置。
-
匿名内部类由于没有名字,而构造方法的方法名必须和类名保持一致,所以匿名内部类没有构造方法。但他内部可以写可以用来初始化的构造代码块。
-
记住匿名内部类的写法创建出来一个对象,这个对象是new后面那个类或者接口的具体子类对象,所以匿名内部类类体里写的是新建对象的属性和方法。
-
当我们对对象进行比较时,一半会重写对象的equals方法,但是要注意,如果重写方法的判定用的是getClass()判断两对象是否是同一个类的,那么用这样的equals方法对匿名子类做判定会产生错误!
lambda表达式
lambda表达式作为一个局部内部类的子类对象,他的语法更简洁,但相对应的,所能使用的范围就更窄,lambda表达式只能作为实现了功能接口的子类对象。
功能接口就是接口中有且只有一个必须要让子类实现的抽象方法的接口。
lambda表达式的语法是:
(形参列表) -> {
// 方法体
}
这里的形参列表是子类必须要实现的那个抽象方法的参数列表,大括号围住的就是要重写的抽象方法体。
这样的写法会产生一个困惑,我们看到在语法中没有一个地方声明过该子类的数据类型,或者说该子类要实现的是哪个功能接口。那么jvm如何判断lambda表达式的数据类型呢?
事实上,编译器会在背后做lambda表达式的类型推断,自动判断数据类型。他的推断依据在于lambda表达式之外的额外信息,或者说lambda表达式所处的上下文。这就跟lambda表达式的使用场景有关了,比如:
-
lambda表达式中除了实现父类接口的抽象方法的方法体外,没有任何其他自有的成员属性和方法,所以在以lambda表达式的形式创建子类对象后可以用父类引用接收他,然后进行使用。这样很明显就找到了lambda表达式的数据类型。
-
有时在创建完lambda表达式后会直接调用该对象实现的方法,这时必须显式声明他的父类名,语法是:
((父接口的名字)Lambda表达式).方法名(实参)。根据这样的方法,编译器就可以判断其实现的是哪个父类接口,以及数据类型了。 -
这个是最常用的。还记得吗,lambda表达式是一个定义在局部位置的子类对象,那么最常定义在局部位置的哪里呢?哪里最需要一个只实现了功能接口的子类对象呢?这里我们就见识到lambda表达式最常用的用法:
将lambda表达式作为一个类似函数的函数体①传入到另一个函数方法②中,从而能让方法②使用这个函数体①。本质上是传入了一个实现函数体①的对象参数,然后调用对象的方法来使用该函数体①。
相当于将一个方法当做参数传给另一个方法!这在以往java中是实现不了这个功能的,但在java8以后就可以了,就是用lambda表达式的形式传入的!这就是lambda表达式的用法!如果你想给一个方法中传入一个外来方法,就用lambda表达式吧!
回归编译器如何对lambda表达式做类型推断,在这里,将lambda表达式这个子类对象当做实参传入方法的时候,方法很显然早就定义好了参数的数据类型。从而能做出推断!而且lambda表达式也能作为一个返回值,也就是说当一个方法想向外传递一个函数的时候,就能返回一个以lambda表达式形式定义的子类对象,那么方法显然也早就定义好了返回值的数据类型(一般都是父类接口),所以这里也很好做出类型推断!
结语
实际上lambda表达式还有更多可讲的内容,比如说lambda表达式的简化,方法回调等等。
但我想一篇文章只聚焦一个重点内容,本次的重点内容就是内部类对象的使用要点。如果意图增添太多内容,只会破坏文章的结构,阅读体验也不会太好,所以点到为止,点到为止!