你说你是高工,匿名内部类有我玩得6吗?

源码共读

共 2164字,需浏览 5分钟

 ·

2019-12-18 23:21

94faa6f89b600caf57eccf18297353a5.webp

黑客技术点击右侧关注,了解黑客的世界!

c30118fcf0c62307b8f7859b15ab05e7.webp

Java开发进阶点击右侧关注,掌握进阶之路!

0774d4335c44123dee1b95e82cf92ed7.webp

Python开发点击右侧关注,探讨技术话题!


作者丨手艺人
来源丨IT烂笔头(nj_android)

fc30e3fc6fd65f76cdb027dbc5e7c81d.webp

阅读文本大概需要 5 分钟。

1

基础知识



匿名内部类大家肯定都很熟悉,如果你是做Android开发的一定再熟悉不过了,因为你学Android的时候写的第二行代码一定是setOnClickListener,第一行代码一般是findViewById。写个简单的匿名内部类:
public abstract class Test {    abstract void onClick();}
Test test = new Test() { @Override    void onClick() {   }};
作为一个初级工程师,一般能使用就ok了。


匿名内部类,顾名思义就是不知道名字的内部类。它真的就没有名字吗?有想过这个问题吗?如果你想过,那证明你是一个不甘于做初级工程师,想往上拔高的人。事实上匿名内部类在被编译成字节码的时候会被定义一个类名,只是类名呢不是那么的被人类所容易阅读,假设上面那个匿名内部类的外部类为OuterClass,编译器编译后就会将上面的匿名内部类定义为:"包名.OuterClass$1",其中里面的'$1'指的是OutterClass类里面的第一个匿名内部类。如果你怀疑它的正确性,可以验证下:
Class testClass = Class.forName("包名.OuterClass$1");System.out.println(testClass);


2

继承结构



以上面举的例子来说,匿名内部类的父类是Test,或者我们常用的setOnclickListener(new OnclickListener{})来说,就是实现OnClickListener接口的匿名内部类。
那么我们能不能同时继承一个类和实现一个接口呢?像这样:
Test|OnClickListener testListener = new Test() implements OnClickListener{    ...}
这种可以吗?在有些语言是支持的,但是呢Java是不支持的。我们知道Java10支持类型推导了,那上面的例子可不可以写成这样呢?
var testListener = new Test() implements OnClickListener{    ...}
可不可以呢?其实也是不可以的,那有同学就讲了,你在这瞎折腾了半天,都是不可以那还讲干啥?聪明的同学可能能从上面也能吸取到一些知识。比如,你可以去查一下哪些语言支持第一种方式,这里只是给你抛砖引玉用的。从第二种方式中我讲到了Java 10支持了类型推导,那你也可以再去查下Java 10到底新增了哪些新特性是不是?那到底能不能实现呢?当然是可以的,你可以使用Java的local class。感兴趣的读者自行查阅一下哦。如果你是中级工程师掌握到这里就蛮不错的了。

3

构造方法



匿名内部的构造方法是谁定义的?很显然开发者并没有机会去定义,是由编译器给我们编译的,在非静态区里面,我们写匿名内部类时会持有外部类的引用,那这个引用编译器会帮我们作为构造方法的参数传进去。举个例子:
4648202a5cbf35b69285e65076ef5901.webp


由于我们的内部类是非静态的所以是需要持有内部类InnerClass的外部类的实例(OuterClass的实例),而我们的匿名内部类也是在非静态的方法区中,那么就会持有匿名内部类$1的外部类的实例(Client的实例),所以编译器给我们的匿名内部类定义的构造方法中带上了两个实例的参数。
那如果我们将内部类换成Interface呢?Interface跟静态内部类的效果一样,就不会引用外部类的实例,所以这时候编译器在定义匿名内部类的构造方法时只会将匿名内部类的外部类实例带入,而不会将内部类的外部类实例带入:
ef77becc06734cec2106c45d312fd90f.webp


如果我们将匿名内部类放在静态的方法中,那么编译器就不会将任何外部类的实例作为构造方法的参数传入了。


还有一个我们在匿名内部类访问局部变量时,需要将局部变量声明为final的。原因是什么呢?因为如果你在匿名内部类访问局部变量的时候,编译器一样会在匿名内部类的构造方法中将其作为参数传进去,不过呢,传进去的时候是当时的一个拷贝,如果不是final的,那么你的代码在后面对变量进行更改的话,那么在匿名内部类中使用的还是旧的值,这样处理显然会有问题,所以Java要求必须使用final来声明。如图:


b839f867f734cbb2b7b3a48e6255e0d1.webp


所以,综上我们知道匿名内部类的构造方法的定义是:
  • 由编译器定义
  • 构造方法的参数
    • 外部类的对象(定义在非静态方法区)
    • 父类的外部类的对象(父类是非静态的)
    • 父类的构造方法参数
    • 外部捕获的变量(方法体引用的局部final变量)


到这里为止,如果你都知道的话,我觉你已经有了高工的思维高度了。

 推荐↓↓↓ 

?16个技术公众号】都在这里!

涵盖:程序员大咖、源码共读、程序员共读、数据结构与算法、黑客技术和网络安全、大数据科技、编程前端、Java、Python、Web编程开发、Android、iOS开发、Linux、数据库研发、幽默程序员等。

c3d70bccc71d161bc82333e2a3edf0c8.webp万水千山总是情,点个 “在看” 行不行
浏览 10
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报