- Java 双重比较
- java - 比较器与 Apache BeanComparator
- Objective-C 完成 block 导致额外的方法调用?
- database - RESTful URI 是否应该公开数据库主键?
假设我有以下代码:
// 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);
}
输出如下所示:
foo(f): 3
foo(i): 2
foo(n): 3
foo(s): Test
现在问题:
foo(n)
调用 foo(Number para)
,很可能是因为 n
被定义为 Number
,即使它有一个 Integer
分配给它。那么我是否正确地假设决定采用哪些重载方法是在编译时发生的,没有动态绑定(bind)? (关于静态和动态绑定(bind)的问题)foo(f)
使用 foo(Number para)
,而 foo(i)
使用 foo(Integer para)
。只有 foo(s)
使用通用版本。因此,编译器总是会查看给定类型是否存在非泛型实现,只有在没有的情况下才会回退到泛型版本吗? (关于泛型的问题)foo(f)
使用 foo(Number para)
,而 foo(i)
使用 foo(Integer para )
。然而,Integer i
也将是一个Number
。那么总是采用继承树中“最外层”类型的方法吗? (关于继承的问题)我知道这些问题很多,而且示例并非来自生产代码,但我只是想知道“背后发生的事情”以及事情发生的原因。
非常感谢任何指向 Java 文档或 Java 规范的链接,我自己找不到它们。
最佳答案
在 language specification 中解释了确定在编译时调用哪个方法签名的规则。 .特别重要的是关于选择 the most specific method 的部分.以下是与您的问题相关的部分:
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
m<sub>1</sub>
is more specific than another applicable methodm<sub>2</sub>
, for an invocation with argument expressionse<sub>1</sub>
, ...,e<sub>k</sub>
, if any of the following are true:
m<sub>2</sub>
is generic, andm<sub>1</sub>
is inferred to be more specific thanm<sub>2</sub>
for argument expressionse<sub>1</sub>
, ...,e<sub>k</sub>
by §18.5.4.
m<sub>2</sub>
is not generic, andm<sub>1</sub>
andm<sub>2</sub>
are applicable by strict or loose invocation, and wherem<sub>1</sub>
has formal parameter types S1, ..., Sn andm<sub>2</sub>
has formal parameter types T1, ..., Tn, the type Si is more specific than Ti for argumente<sub>i</sub>
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).
在这种情况下,Integer
比 Number
更具体因为Integer
延伸Number
,因此每当编译器检测到对 foo
的调用时接受类型为 Integer
的变量已声明 , 它将添加对 foo(Integer)
的调用.
更多关于第一个条件与第二个方法是通用的在 this section 中解释。 .这有点冗长,但我认为重要的部分是:
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
m<sub>1</sub>
be the first method andm<sub>2</sub>
be the second method. Wherem<sub>2</sub>
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
m<sub>1</sub>
is more specific thanm<sub>2</sub>
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.)
这基本上意味着 foo(Number)
和 foo(Integer)
都比 foo(T)
更具体因为编译器可以为生成 Number
的泛型方法(例如 foo(Number)
本身)推断至少一种类型和 foo(Integer)
更具体(这是因为 Integer <: Number
和 Number <: Number
)。
这也意味着在您的代码中 foo(T)
仅适用于传递 String
的调用(并且本质上是最具体的方法,因为它是唯一适用的方法) .
关于Java方法重载 - 同一继承树中的通用参数和参数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35924313/
假设我有一个类,我在其中重载了运算符 == : Class A { ... public: bool operator== (const A &rhs) const; ... };
我知道你不应该使用 std::find(some_map.begin(), some_map.end()) 或 std::lower_bound,因为它会采用线性时间而不是 some_map.lowe
我正在尝试在 Haskell 中定义 Vector3 数据类型,并允许在其上使用 (+) 运算符。我尝试了以下方法: data Vector3 = Vector3 Double Double Doub
我已经为我的类图将运算符重载为“-”。它的用途并不完全直观(糟糕的编码 - 我知道)但是如果我做 graph3 = graph2-graph1 那么图 3 是应该只接收图 2 和图 1 中的那些顶点。
这个问题在这里已经有了答案: 关闭 11 年前。 Possible Duplicate: Operator overloading 我想重载 以按字母顺序排列字符串,但我不确定该怎么做。 如何再次
下面的代码给我一个编译错误。谁能告诉我为什么? class mytype { public: int value; mytype(int a) { value = a;
这有什么问题吗? class Vec2 attr_accessor :x, :y # ... def += (v) @x += v.x @y += v.y retu
是否可以重载 [] 运算符两次?允许这样的事情:function[3][3](就像在二维数组中一样)。 如果可能的话,我想看看一些示例代码。 最佳答案 您可以重载 operator[] 以返回一个对象
我的团队目前正在与 Lua 合作,创建一个 android 游戏。我们遇到的一件事是表面上无法创建重载构造函数。 我习惯于使用默认值设置一个对象,然后在需要时使其过载。 前任: apples() {
我有一个网页,在某个时候显示一个导航栏,它只不过是一个 a 元素的列表 (ul)。所述 a 元素的大多数样式规则都是通用的。唯一应该改变的部分是要显示的图像,可以从列表中每个 li 元素的 id 标签
我对使用/重载“范围步长”运算符(.. ..)很感兴趣,但我终其一生都无法了解如何使用它。 在文档中它说 // Usage: start .. step .. finish 但是在 F# shell
Java 11(可能无关紧要): public static String toString(Object obj) { return ReflectionToStringBuilder.to
就目前而言,这个问题不适合我们的问答形式。我们希望答案得到事实、引用或专业知识的支持,但这个问题可能会引起辩论、争论、投票或扩展讨论。如果您觉得这个问题可以改进并可能重新打开,visit the he
我无法理解以下代码(针对行号进行注释) class Base { void m1(Object o) { } void m2(String o) { } } publi
我有以下代码片段: #include using namespace std; struct Integer{ int x; Integer(const int val) : x(v
class myclass{ //definitions here }; myclass e; int myarray[10]; /* Do something... */ e = myarray;
为什么不能将下标运算符(operator [])作为 friend 函数重载? 最佳答案 正如Bjarne Stroustrup在D&E book中所说: However, even in the o
我有以下代码片段: #include using namespace std; struct Integer{ int x; Integer(const int val) : x(v
因此,我有一个问题是我最近尝试重载 namespace Eng { /** * A structure to represent pixels */ typedef
如何重载onResume()以正确的方式工作?我想从 activity 返回到 MainActivity ,我希望在其中具有与应用程序启动后相同的状态。我想使用 recreate() 但它循环了或者类
我是一名优秀的程序员,十分优秀!