繁体   English   中英

java 如何确定,当一个方法具有泛型类型参数而另一个具有非泛型参数时,将调用哪个重载方法?

[英]How does java determine, which overloaded method will call, when one method with generic type parameter and other with non generic parameter?

Java class 不能有两个在类型擦除后具有相同签名的重载方法。 Java 将在以下情况下生成编译时错误:

public class Example {
    public void print(Set<String> strSet) { }
    public void print(Set<Integer> intSet) { }
}

但我的问题不是这样。 有两种情况1和2。我想知道,为什么他们的行为不一样?

Scenario 1

import java.util.ArrayList;
import java.util.List;

public class MethodOverload {

    public static void main(String[] args) {
        
        MethodOverload methodOverload = new MethodOverload();

        //Point 1 - List here
        List<Integer> mangoList = new ArrayList<Integer>();
        mangoList.add(1);
        mangoList.add(2);
        
        System.out.println("Size List<Integer>:"+methodOverload.count(mangoList));
        
        //Point 2 - ArrayList here
        ArrayList<String> nameList = new ArrayList<String>();
        nameList.add("Anex");
        nameList.add("Alia");
        nameList.add("Maxim");
        
        System.out.println("Size List<String>:"+methodOverload.count(nameList));
    }
    
    public <T> int count(List<T> list) {
        System.out.print("Ïn side generic; ");
        return list.size();
    }
}

Output,对于场景 1:

Inside generic block; Size List<Integer> : 2
Inside generic block; Size List<String>  : 3

Scenario 2:

import java.util.ArrayList;
import java.util.List;

public class MethodOverload {

    public static void main(String[] args) {
        
        MethodOverload methodOverload = new MethodOverload();

        //Point 3 - List here
        List<Integer> mangoList = new ArrayList<Integer>();
        mangoList.add(1);
        mangoList.add(2);
        
        System.out.println("Size List<Integer>:"+methodOverload.count(mangoList));
        
        //Point 4 - ArrayList here
        ArrayList<String> nameList = new ArrayList<String>();
        nameList.add("Anex");
        nameList.add("Alia");
        nameList.add("Maxim");
        
        System.out.println("Size List<String>:"+methodOverload.count(nameList));
    }
    
    public <T> int count(List<T> list) {
        System.out.print("Inside generic block; ");
        return list.size();
    }
    
    //Point 5
    public int count(ArrayList<String> list) {
        System.out.print("Inside non-generic block; ");
        return list.size();
    }

}

Output,对于场景 2:

Inside generic block;     Size List<Integer> : 2
Inside non-generic block; Size List<String>  : 3

现在,我的问题是,java 如何决定在scenario 2的情况下调用哪个方法? scenario 1中,java 调用单计数方法没有任何问题,那么为什么 java 不显示scenario 2的类似行为?

在 Java 中,方法参数类型是方法签名的一部分

因此,该方法将在编译时根据参数类型进行绑定。 (这不应被误解为调用方法的参考变量的多态行为,比如methodOverload

在上面的代码中,

  1. 引用类型为List的第一个调用将绑定到以List作为参数类型的方法
  2. 让我们假设,没有以List作为参数类型的方法,并假设存在以ArrayList作为参数的方法。 编译器如何保证List在运行时实际上是一个ArrayList以维护类型安全承诺。 既然不能,那就禁止这种绑定。
  3. ArrayList类型的第二次调用将绑定到具有确切类型ArrayList的方法(如果存在)。 如果该方法不存在,则将其映射到List方法(如果考虑 generics,则具有相关的泛型)

本质上,

  1. 将调用其方法的 object 由调用该方法的引用变量的多态性决定(编译器确保仅引用变量类型 class 在编译时具有相关方法,但不控制在运行时调用子类方法)
  2. 一旦 object 被运行时识别,在该 object 上调用的方法由基于参数的编译时绑定控制。

最后的想法

  1. 覆盖是由调用方法的引用的 object 类型确定的运行时行为
  2. 重载是 object 的编译时确定性行为,由时间根据参数类型选择。

因为类型擦除仅适用于泛型类型。 Java 不会“擦除”inheritance。

在场景 1 中,count() 方法签名在运行时有效地变为:

public int count(List<Object> list) {...}

因此 mangoList(声明为 List)和 nameList(声明为 ArrayList)都符合此签名。

在场景 2 中,方法签名在运行时有效地变为:

public int count(List<Object> list) {...}

public int count(ArrayList<Object> list) {...}
  • 由于 mangoList 被声明为 List,因此 Java 适合第一个 count() 方法,将 List 作为参数。
  • 由于 nameList 被声明为 ArrayList,因此 Java 适合第二个 count() 方法,将 ArrayList 作为参数。

Java 早在 generics 出现之前就知道如何执行此(基于继承的)重载。 它不能做的是基于未绑定的泛型类型的重载,因为运行时类型擦除,这就是编译器阻止你这样做的原因。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM