目录戳这里java
方法重载在面向对象里很基础,不论是学校仍是自学时都会教到这个概念。实际上相互重载的方法除了名字相同以外,并无任何关系,由于它们拥有彻底不一样的方法签名。git
JVM的invokeXXX
字节码(除了invokedynamic
,其用法前文已写),都须要一个方法签名来指定调用哪一个方法。例如github
new java/lang/Object dup invokespecial java/lang/Object:<init>()V
先建立一个java.lang.Object
对象,而后再栈中复制这个“未初始化的对象”,接着调用构造函数进行初始化。函数
方法签名由两部分构成,.net
java/lang/Object
descriptor
然后者又由两部分组成code
(Ljava/lang/String;I)
就表明了(java.lang.String, int)
Ljava/util/regex/Pattern;
因此说,两个重载方法之间,对于JVM来讲一点关系都没有,对于使用者(咱们)来讲,相关联的仅仅是名称相同而已。编译中要作的事情是根据方法名称和给定的参数类型‘定位’到要调用的方法。对象
从直观上就能够看出,想找到要调用的方法有这几个因素须要知足:blog
方法可以被访问到ip
方法名称一致ci
方法参数长度一致
每一个参数(parameter)的类型都要可以由变量(argument)转换而来,例如
String.valueOf('abc')
其中'abc'
是字符串,就不能匹配到String.valueOf(int)
这个方法
(最关键的一点)方法应当是最佳的。
什么是最佳的方法呢?例如java.util.List
有这两个方法
remove(int) remove(Object)
咱们都知道,若是给出int
型,就会调用前者,若是给出引用类型,例如java.lang.Integer
,就会调用后者。比方说:
list = [1,2,3] list.remove(1)
最后结果是[1,3]
。若是是
list.remove(Integer(1))
最后结果是[2,3]
。
这个例子是基本类型和引用类型间的,它们区别比较大,再看一个同为引用类型的例子:
class List concat(o : Object) : List concat(list : List) : List
上述定义的concat
方法,一个接收Object
类型,一个接收List
类型。在调用时,若是给出list.concat('abc')
,就须要调用Object
的重载,若是给出list.concat([1,2,3])
就须要调用List
的重载。
固然,若是把List向上转换为Object
再来调用,好比list.concat([1,2,3] as Object)
会调用Object
的重载,由于编译器只管给定的类型,不会无论(有的时候也不可能知道)确切的类型。
在编译时(动态类型语言在运行时也)要保证调用的方法“最佳”。我定义的“最佳”是: 在转换到目标类型所需的“距离”最短。
这里的“距离”在以前 step 3 检查合法性时就提到过了。向上转型一次,距离就+1。而int
到Integer
这样的转换是“隐式类型转换”,须要加上一个常数,好比10000
,一个比较大的数字,保证正常的向上转型永远不会大于隐式转换,也即隐式转换优先级更低。
最终能够获得一些这样的“元组”,表示每一个参数的转换次数
例如:方法为(String, Object)
和(CharSequence, Object)
参数和对应元组:
(String, List) -- (0, 1)/(1, 1)
这样一来,前者每一个数字都不大于后者,因此应当调用前者。
可是若是有这种状况方法为(String, Object)
和(CharSequence, List)
(String, List) -- (0, 1)/(1, 0)
那就没法肯定了,由于前._0
< 后._0
可是 前._1
> 后._1
。Java也是如此,若是有兴趣能够尝试一下。
void a(CharSequence c, List list){} void a(String c, Object list){} a("a",new ArrayList());
会报错:两个方法都匹配。因此有的地方才会在方法调用参数上加一个类型。
那么若是出现须要“隐式转换”的状况呢?只要加上一个比较大的常数便可。实际没什么太大区别。
放个代码:编译期的方法匹配戳这里。编译期没有彻底的使用本篇的距离,而是直接判断“好坏”. 运行时的方法寻找戳这里,这和本篇说的方式如出一辙,由于有隐式类型转换的存在,因此不得不加个距离。
最后,但愿看官可以关注个人编译器哦~Latte