Java方法重载 - 通用参数&参数在同一继承树内 [英] Java method overloading - Generic parameter & parameters within same inheritance tree
问题描述
//方法接受通用参数
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
现在问题:
-
foo(n)
调用foo(Number para)
,很可能是因为n
被定义为Number
,即使指定了Integer
到它。那么我是否正确地假设决定哪个重载方法在编译时发生,而没有动态绑定? (关于 static 和 dynamic 绑定的问题) -
foo(f)
使用foo(Number para)
,而foo(i)
使用foo(Integer para)
。只有foo(s)
使用通用版本。因此,编译器总是会查看给定类型是否存在非泛型实现,并且只有在它不回退到泛型版本时? (有关泛型的问题) - 同样,
foo(f)
使用foo (Number para)
,而foo(i)
使用foo(整数段)
。然而,Integer i
也可能是Number
。那么总是在继承树中使用最外层类型的方法? (关于继承的问题)
我知道这些是很多问题,高效的代码,但我只想知道背后发生了什么以及为什么会发生。
还有任何指向Java文档或Java规范的链接都非常感谢,我自己找不到它们。
如果多个成员方法都可访问且适用于方法调用,选择一个为运行时方法调度提供描述符。 Java编程语言使用方法被选择的规则。
...
一个适用的方法
m 1
比另一个适用方法m 2 <对于具有参数表达式
是通用的,并且e 1
,...,e的调用,可以使用子>
b code> m 2m 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)$ c更具体$ c>,因为编译器至少可以为泛型方法推导出一个类型(例如
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):
foo(n)
callsfoo(Number para)
, most probably becausen
is defined asNumber
, even though it has anInteger
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)foo(f)
usesfoo(Number para)
, whilefoo(i)
usesfoo(Integer para)
. Onlyfoo(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)- Again,
foo(f)
usesfoo(Number para)
, whilefoo(i)
usesfoo(Integer para)
. Yet,Integer i
would also be aNumber
. 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 methodm2
, for an invocation with argument expressionse1
, ...,ek
, if any of the following are true:
m2
is generic, andm1
is inferred to be more specific thanm2
for argument expressionse1
, ...,ek
by §18.5.4.
m2
is not generic, andm1
andm2
are applicable by strict or loose invocation, and wherem1
has formal parameter types S1, ..., Sn andm2
has formal parameter types T1, ..., Tn, the type Si is more specific than Ti for argumentei
for all i (1 ≤ i ≤ n, 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 andm2
be the second method. Wherem2
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 thanm2
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方法重载 - 通用参数&参数在同一继承树内的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!