前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >你说你是高工,匿名内部类有我玩得6吗?

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

作者头像
吴延宝
发布2019-12-19 11:28:24
4990
发布2019-12-19 11:28:24
举报

阅读文本大概需要 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

构造方法

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

由于我们的内部类是非静态的所以是需要持有内部类InnerClass的外部类的实例(OuterClass的实例),而我们的匿名内部类也是在非静态的方法区中,那么就会持有匿名内部类$1的外部类的实例(Client的实例),所以编译器给我们的匿名内部类定义的构造方法中带上了两个实例的参数。

那如果我们将内部类换成Interface呢?Interface跟静态内部类的效果一样,就不会引用外部类的实例,所以这时候编译器在定义匿名内部类的构造方法时只会将匿名内部类的外部类实例带入,而不会将内部类的外部类实例带入:

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

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

所以,综上我们知道匿名内部类的构造方法的定义是:

  • 由编译器定义
  • 构造方法的参数
    • 外部类的对象(定义在非静态方法区)
    • 父类的外部类的对象(父类是非静态的)
    • 父类的构造方法参数
    • 外部捕获的变量(方法体引用的局部final变量)

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

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-12-08,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 IT烂笔头 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档