面试题答案
一键面试编译期确定方法版本的过程
- 静态类型决定调用范围:在Java中,编译期是根据对象的静态类型(即声明对象时使用的类型)来确定可调用的方法集合。例如,如果有一个
Animal
类,Dog
类继承自Animal
,并且代码中声明Animal animal = new Dog();
,那么编译期会根据Animal
类型去查找其及其父类和所实现接口中定义的方法。 - 重载方法的选择:对于重载的方法,编译期通过参数列表的静态类型来确定具体调用的版本。编译器会从候选的重载方法集中选择一个最精确匹配的方法。例如,假设有方法
void eat(Food food)
和void eat(Meat meat)
,如果调用eat(new Steak())
(Steak
继承自Meat
,Meat
继承自Food
),编译器会选择void eat(Meat meat)
,因为Meat
比Food
更精确匹配Steak
。 - 重写方法的初步确定:对于重写的方法,编译期先根据静态类型找到在该类型及其父类层次结构中定义的方法。但最终运行时实际调用的重写版本由对象的实际类型(运行时类型)决定。编译期只是确保调用的合法性,即被调用的方法在静态类型中有定义,并且符合访问权限。
类型擦除对编译时多态解析的影响
- 泛型相关方法重载:在泛型存在的情况下,类型擦除会影响编译时对重载方法的解析。由于类型擦除,泛型参数在编译后会被擦除为其限定类型(通常是
Object
,如果没有显式限定)。例如,假设有两个方法void process(List<String> list)
和void process(List<Integer> list)
,编译后这两个方法的参数类型都变为List
,这会导致编译错误,因为擦除后方法签名相同,无法构成重载。 - 泛型相关重写:对于实现泛型接口或继承泛型类并重写方法的情况,类型擦除使得编译器在编译时以擦除后的类型来验证重写的正确性。例如,类
MyList<T> implements List<T>
,重写add(T e)
方法,编译时会按照擦除后的add(Object e)
(假设T
没有显式限定)来验证是否符合重写规则,如方法签名(除了参数类型被擦除)、返回类型(在协变返回类型支持下)和访问权限等。