Java方法重载 - 通用参数&参数在同一继承树内 [英] Java method overloading - Generic parameter & parameters within same inheritance tree

查看:144
本文介绍了Java方法重载 - 通用参数&参数在同一继承树内的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

  //方法接受通用参数
public static< T> T foo(T para){
return para;
}

//接受整数参数的方法
public static Integer foo(Integer para){
return para + 1;
}

//接受方法数字参数
public static Number foo(Number para){
return para.intValue()+ 2;


public static void main(String [] args){
Float f = new Float(1.0f);
整数i =新的整数(1);
数字n =新的整数(1);
String s =Test;

数字fooedFloat = foo(f); //使用foo(Number para)
Number fooedInteger = foo(i); //使用foo(整数段)
数字fooedNumber = foo(n); //使用foo(Number para)
String fooedString = foo(s); //使用foo(T para)

System.out.println(foo(f):+ fooedFloat);
System.out.println(foo(i):+ fooedInteger);
System.out.println(foo(n):+ fooedNumber);
System.out.println(foo(s):+ fooedString);
}

输出结果如下:

  foo(f):3 
foo(i):2
foo(n):3
foo(s):Test

现在问题:


  1. foo(n)调用 foo(Number para),很可能是因为 n 被定义为 Number ,即使指定了 Integer 到它。那么我是否正确地假设决定哪个重载方法在编译时发生,而没有动态绑定? (关于 static dynamic 绑定的问题)

  2. foo(f)使用 foo(Number para),而 foo(i)使用 foo(Integer para) 。只有 foo(s)使用通用版本。因此,编译器总是会查看给定类型是否存在非泛型实现,并且只有在它不回退到泛型版本时? (有关泛型的问题

  3. 同样, foo(f)使用 foo (Number para),而 foo(i)使用 foo(整数段)。然而, Integer i 也可能是 Number 。那么总是在继承树中使用最外层类型的方法? (关于继承的问题

我知道这些是很多问题,高效的代码,但我只想知道背后发生了什么以及为什么会发生。



还有任何指向Java文档或Java规范的链接都非常感谢,我自己找不到它们。

se8 / html / jls-15.html#jls-15.12.2rel =nofollow>语言规范。特别重要的是选择最具体的方法。以下是与您的问题相关的部分:


如果多个成员方法都可访问且适用于方法调用,选择一个为运行时方法调度提供描述符。 Java编程语言使用方法被选择的规则。



...



一个适用的方法 m 1 比另一个适用方法 m 2 <对于具有参数表达式 e 1 ,..., e的调用,可以使用子> b code> m 2 是通用的,并且 m 1 被推断为比 m 2 更具体的参数表达式 e 1 ,..., e k 通过§18.5.4。


  • < > m 2 不是通用的, >和 m 2 适用于严格或松散调用,其中 m 1 具有形式参数类型S1,...,Sn和 m 2 具有形式参数类型T 1 ,。 ...,T n ,类型S i 比参数 e i的T 更具体。 n = k )。




  • ...

    b $ b

    如果S<:T(§4.10)。

    $,则类型S比任何表达式的类型T更具体。 b
    $ b

    在这种情况下, Integer Number 更具体,因为 Integer 扩展 Number ,所以无论何时编译器检测到对 foo 的调用,需要一个类型的变量类型的变量,它会为 foo(Integer)添加一个调用>。



    有关第二种方法是泛型的第一个条件的更多信息,请参见本节。它有点冗长,但我认为其中最重要的部分是:

    lockquote

    当测试一个适用的方法是更具体时 (第15.12.2.5节),其中第二种方法是通用的,有必要测试是否可以推断出第二种方法的类型参数的某些实例化,以使第一种方法比第二种方法更具体。



    $ p
    $ b $ m 1 是第一种方法,第二种方法是 m 2 。其中 m 2 具有类型参数P 1 ,...,P p ,假设α1,...,αp是推理变量,并且令θ是替换[P 1::=α 1 ,...,P :=α p ]。


    ...

    确定 m 1 的过程比<$ c $更具体c> m 2 如下:

    ...



    如果T i 是一个合适的类型,那么结果是 true 如果S i 比T i更具体(§15.12.2.5),否则为 false 。 (注意,S i 总是一个合适的类型。)

    基本上意味着 foo(Number) foo(Integer)都比 foo(T),因为编译器至少可以为泛型方法推导出一个类型(例如 Number 本身),这使得 foo(Number) foo(整数)更具体(这是因为整数<:Number Number <:Number )。



    这也意味着在您的代码 foo(T) code>只适用于通过 String 的调用,并且它本质上是最具体的方法,因为它只是适用的一个。


    Let's assume I have following code:

    // Method acception generic parameter
    public static <T> T foo(T para) {
        return para;
    }
    
    // Method accepting Integer parameter
    public static Integer foo(Integer para) {
        return para + 1;
    }
    
    // Method accepting Number parameter
    public static Number foo(Number para) {
        return para.intValue() + 2;
    }
    
    public static void main(String[] args) {
        Float f = new Float(1.0f);
        Integer i = new Integer(1);
        Number n = new Integer(1);
        String s = "Test";
    
        Number fooedFloat = foo(f);     // Uses foo(Number para)
        Number fooedInteger = foo(i);   // Uses foo(Integer para)
        Number fooedNumber = foo(n);    // Uses foo(Number para)
        String fooedString = foo(s);    // Uses foo(T para)
    
        System.out.println("foo(f): " + fooedFloat);
        System.out.println("foo(i): " + fooedInteger);
        System.out.println("foo(n): " + fooedNumber);
        System.out.println("foo(s): " + fooedString);
    }
    

    The output looks the following:

    foo(f): 3
    foo(i): 2
    foo(n): 3
    foo(s): Test
    

    Now the question(s):

    1. foo(n) calls foo(Number para), most probably because n is defined as Number, even though it has an Integer assigned to it. So am I right in the assumption that the decision, which of the overloaded methods is taken happens at compile-time, without dynamic binding? (Question about static and dynamic binding)
    2. foo(f) uses foo(Number para), while foo(i) uses foo(Integer para). Only foo(s) uses the generic version. So the compiler always looks if there is a non-generic implementation for the given types, and only if not it falls back to the generic version? (Question about generics)
    3. Again, foo(f) uses foo(Number para), while foo(i) uses foo(Integer para). Yet, Integer i would also be a Number. So always the method with the "outer-most" type within the inheritance tree is taken? (Question about inheritance)

    I know these are a lot questions, and the example is not taken from productive code, yet I just would like to know what "happens behind" and why things happen.

    Also any links to the Java documentation or the Java specification are really appreciated, I could not find them myself.

    解决方案

    The rules of determining which method signature to call at compile-time are explained in the language specification. Specifically important is the section on choosing the most specific method. Here are the parts related to your questions:

    If more than one member method is both accessible and applicable to a method invocation, it is necessary to choose one to provide the descriptor for the run-time method dispatch. The Java programming language uses the rule that the most specific method is chosen.

    ...

    One applicable method m1 is more specific than another applicable method m2, for an invocation with argument expressions e1, ..., ek, if any of the following are true:

    • m2 is generic, and m1 is inferred to be more specific than m2 for argument expressions e1, ..., ek by §18.5.4.

    • m2 is not generic, and m1 and m2 are applicable by strict or loose invocation, and where m1 has formal parameter types S1, ..., Sn and m2 has formal parameter types T1, ..., Tn, the type Si is more specific than Ti for argument ei for all i (1 ≤ in, n = k).

    ...

    A type S is more specific than a type T for any expression if S <: T (§4.10).

    In this case, Integer is more specific than Number because Integer extends Number, so whenever the compiler detects a call to foo that takes a variable declared of type Integer, it will add an invocation for foo(Integer).

    More about the first condition related to the second method being generic is explained in this section. It's a little verbose but I think the important part is:

    When testing that one applicable method is more specific than another (§15.12.2.5), where the second method is generic, it is necessary to test whether some instantiation of the second method's type parameters can be inferred to make the first method more specific than the second.

    ...

    Let m1 be the first method and m2 be the second method. Where m2 has type parameters P1, ..., Pp, let α1, ..., αp be inference variables, and let θ be the substitution [P1:=α1, ..., Pp:=αp].

    ...

    The process to determine if m1 is more specific than m2 is as follows:

    ...

    If Ti is a proper type, the result is true if Si is more specific than Ti for ei (§15.12.2.5), and false otherwise. (Note that Si is always a proper type.)

    Which basically means that foo(Number) and foo(Integer) are both more specific than foo(T) because the compiler can infer at least one type for the generic method (e.g. Number itself) that makes foo(Number) and foo(Integer) more specific (this is because Integer <: Number and Number <: Number) .

    This also means that in your code foo(T) is only applicable (and inherently the most specific method since it's only the one applicable) for the invocation that passes a String.

    这篇关于Java方法重载 - 通用参数&amp;参数在同一继承树内的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

    查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆