MST

星途 面试题库

面试题:Java多态重载方法选择与泛型的复杂交互

当Java多态遇上泛型时,编译器对重载方法的选择会变得更加复杂。请详细阐述在泛型类、泛型方法以及通配符存在的情况下,编译器如何选择合适的重载方法,并给出具有深度和复杂性的代码示例及分析。
45.6万 热度难度
编程语言Java

知识考点

AI 面试

面试题答案

一键面试

编译器选择重载方法的规则

  1. 类型擦除:Java的泛型是通过类型擦除实现的。在编译时,所有泛型类型参数都会被替换为它们的限定类型(如果有),或者Object类型。这意味着在运行时,泛型信息是不存在的。编译器在选择重载方法时,基于擦除后的类型进行匹配。
  2. 泛型类:对于泛型类中的方法,编译器会先根据调用方法的对象的静态类型来确定可能适用的方法集。如果泛型类有多个重载方法,这些方法的泛型参数不同,编译器会尝试根据实际传入的参数类型来选择最合适的方法。例如,如果一个泛型类GenericClass<T>有两个重载方法void method(T t)void method(String s),当使用GenericClass<Integer>调用method方法时,由于Integer不是String的子类型,编译器会选择void method(T t)方法。
  3. 泛型方法:在泛型方法的情况下,编译器首先会尝试推断泛型方法的类型参数。它会根据传入的实际参数类型来推断类型参数。如果可以通过推断得到明确的类型参数,编译器会选择该泛型方法。如果有多个泛型方法都适用,编译器会根据方法签名的匹配程度来选择。例如,有两个泛型方法static <T> void method(T t)static <T extends Number> void method(T t),当传入一个Integer对象时,两个方法都适用,但编译器会选择更具体的static <T extends Number> void method(T t)方法,因为IntegerNumber的子类型,这个方法的类型参数限定更具体。
  4. 通配符:通配符?用于表示不确定的类型。在方法调用中,通配符类型会影响编译器对重载方法的选择。例如,有方法void method(List<String> list)void method(List<?> list),当调用method方法并传入一个List<Integer>时,只能匹配void method(List<?> list)方法,因为List<Integer>不是List<String>的子类型,但它是List<?>的子类型。

代码示例及分析

class GenericClass<T> {
    // 泛型类中的泛型方法
    public <U> void method(U u) {
        System.out.println("GenericClass method with generic parameter U: " + u);
    }

    // 泛型类中的重载泛型方法,类型参数有界
    public <U extends Number> void method(U u) {
        System.out.println("GenericClass method with bounded generic parameter U: " + u);
    }

    // 泛型类中的非泛型方法
    public void method(String s) {
        System.out.println("GenericClass method with String parameter: " + s);
    }
}

class WildcardExample {
    // 方法接受具体类型的List
    public static void method(List<String> list) {
        System.out.println("method with List<String> parameter");
    }

    // 方法接受通配符类型的List
    public static void method(List<?> list) {
        System.out.println("method with List<?> parameter");
    }
}

public class PolymorphismWithGenerics {
    public static void main(String[] args) {
        GenericClass<Integer> gc = new GenericClass<>();
        gc.method(10); // 调用泛型类中类型参数有界的泛型方法,因为10是Number的子类型
        gc.method("Hello"); // 调用泛型类中非泛型方法,因为参数类型是String

        List<String> stringList = Arrays.asList("a", "b");
        List<Integer> integerList = Arrays.asList(1, 2);
        WildcardExample.method(stringList); // 调用接受List<String>的方法
        WildcardExample.method(integerList); // 调用接受List<?>的方法,因为List<Integer>不是List<String>的子类型,但它是List<?>的子类型
    }
}
  1. GenericClass中的方法调用
    • 当调用gc.method(10)时,由于10Integer类型,而IntegerNumber的子类型,所以编译器会选择public <U extends Number> void method(U u)方法。
    • 当调用gc.method("Hello")时,因为参数类型是String,所以编译器会选择public void method(String s)方法。
  2. WildcardExample中的方法调用
    • 当调用WildcardExample.method(stringList)时,由于stringListList<String>类型,所以编译器会选择public static void method(List<String> list)方法。
    • 当调用WildcardExample.method(integerList)时,由于List<Integer>不是List<String>的子类型,但它是List<?>的子类型,所以编译器会选择public static void method(List<?> list)方法。

通过上述示例和分析,可以看到在泛型类、泛型方法以及通配符存在的情况下,编译器如何根据类型擦除、类型推断和方法签名匹配来选择合适的重载方法。