今天被人问了一个问题,然后我去查了源码,虽然解决了问题,但进而引发我另一个思考——如何理解对象赋值给一个接口。
众所周知,如果一个对象赋值给它的接口,那么这个用接口“接”的对象只有接口所定义的方法和属性,失去了原本对象独有的方法和内容。(当然也规范了代码,不过本文暂不讨论这些)
按我之前的理解就是——如果把原对象比作一辆拥有众多功能的跑车,那么接口就是大家统一规格的小汽车。如果把这“跑车”赋值给小汽车,那么势必会失去一些独有的功能和属性。
但原来的属性和方法真会消失吗?
我突然又想到很多框架为了通用性,方法返回值常常为Object,那如果是Object,失去了原来的的属性和方法,只保留Object的属性和方法的话,那框架如何继续操作呢?
这显然是不对,因此我进行了简单的测试:
这里我们看到就算我们用接口接对象,对象依旧能够通过强转回原来的样子,而就算是接口Object,打印时依旧会输出这个对象的值。
这已经充分地说明了一点——用接口接对象,其属性和方法并没有丢失。
那我们该如何理解呢?
实际上我们得从Java的对象理解开始说起。
对象是什么?
学过c语言的小伙伴都知道c中有个叫做指针的东西(相当于指向内存地址的坐标),java相对于c来讲的一大“优势”(有人可能认为是优势,也有人可能认为是劣势)就是Java中没有指针,这极大的减少了因指针滥用的风险。
但java中真的没有指针吗?
Java中处处都是指针!
没错,Java处处都是指针,Java的对象就是指针,这也是我们为什么要复制一个相同的对象时得用clone方法,这也是我们为什么比较两对象值是否相等时要用equals方法而不是==。
因为对象就是指针!!!
以下是我在网上找到的一段话,概括的挺好的,分享给大家
JAVA中的对象类型本质上应该叫做 对象指针 类型。那么传统的对象类型呢?在JAVA里已经不见了踪影!
既然没有了传统的对象类型,那么 对象指针变量 前面的也就可以不要了。对象指针变量也就可以简称为对象变量了,
反正也不会和其它概念混淆! 所有的对象变量都是指针,没有非指针的对象变量,想不用指针都不行,这就是指针的泛化和强化。
不叫指针了,就叫对象变量,这就是概念上的淡化和弱化。 没有了指针的加减运算,也没有了*、->等运算符,这是对指针的简单化。
既然清楚了Java中对象即是指针,那么我们回到现在的问题上来。
如果把一个对象赋值给一个接口,那么实际上会发生什么呢?
真的去重新开辟了块内存空间,再把原来的属性值装进这个空间吗?
不不不,对象只是指针,对象的赋值只是指针的复制而已,把对象赋值给接口也只是把这个对象的地址给这个接口而已!
那么对象实际改变了吗?
并没有!
那改变了什么呢?
改变的只是程序“解析”(这里我愿意用解析一词来形容)这个对象或者说这个指针所指向的内存空间而已!
因为“解析的格式”(或者说解析的标准),对于一些不认识的属性和方法你当然就不能使用喽!
总结
对象赋给接口只是指针而已,至于为什么不能使用原来对象中的方法和属性,当然是因为“解析”的格式不同导致程序根本不认识接口中没有的方法和属性喽!
建议
虽然Java中没有指针的概念,但是最好把它看做指针,这对于你理解底层机制或者算法结构都有很多好处,也让你避免出现诸如为什么对象复制要用clone方法之类的疑惑了。
-----8月8日更新-------
自从小小的接触了下字节码后,对这个问题有了新的理解
方法在编译器会在编译阶段就进行静态分派。指向的还是父类的类,父类里面没有test3()方法
按照这个思路,赋值给接口后,虽然指向的还是实现类,但是编译器会去找其接口(也就是它的静态类型)
详细可以去看这篇文章https://www.cnblogs.com/bigbaby/p/12348956.html
---------11月13日更新----------------
随着自己技术的成长,对此问题有了更深层次的理解,所以重新写了另一篇文章如何理解子类对象赋值给父类(深入理解动态绑定、静态绑定)