从零开始开发JVM语言(十二)重载方法的选择

目录戳这里java

方法重载在面向对象里很基础,不论是学校仍是自学时都会教到这个概念。实际上相互重载的方法除了名字相同以外,并无任何关系,由于它们拥有彻底不一样的方法签名。git

JVM的invokeXXX字节码(除了invokedynamic,其用法前文已写),都须要一个方法签名来指定调用哪一个方法。例如github

new java/lang/Object
dup
invokespecial java/lang/Object:<init>()V

先建立一个java.lang.Object对象,而后再栈中复制这个“未初始化的对象”,接着调用构造函数进行初始化。函数

方法签名由两部分构成,.net

  1. 方法所在类型,例如 java/lang/Object
  2. 方法的descriptor

然后者又由两部分组成code

  1. 方法参数,用小括号包围,例如 (Ljava/lang/String;I)就表明了(java.lang.String, int)
  2. 方法返回值,也是一个类型,例如 Ljava/util/regex/Pattern;

因此说,两个重载方法之间,对于JVM来讲一点关系都没有,对于使用者(咱们)来讲,相关联的仅仅是名称相同而已。编译中要作的事情是根据方法名称和给定的参数类型‘定位’到要调用的方法。对象

从直观上就能够看出,想找到要调用的方法有这几个因素须要知足:blog

  1. 方法可以被访问到ip

  2. 方法名称一致ci

  3. 方法参数长度一致

  4. 每一个参数(parameter)的类型都要可以由变量(argument)转换而来,例如

    String.valueOf('abc')

    其中'abc'是字符串,就不能匹配到String.valueOf(int)这个方法

  5. (最关键的一点)方法应当是最佳的。

什么是最佳的方法呢?例如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。而intInteger这样的转换是“隐式类型转换”,须要加上一个常数,好比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

相关文章
相关标签/搜索