我试图弄清楚Java如何选择执行哪种方法:
//Example 1 prints Square:add(Figure)
Figure fs = new Square();
fs.add(fs);
//Example 2 prints Square:add(Figure)
Rectangle rs = new Square();
rs.add(fs);
//Example 3 prints Rectangle:add(Rectangle). Expected Square:add(Square)
rs.add(new Square());
//Example 4 prints Rectangle:add(Rectangle). Expected Square:add(Figure)
Square ss = new Square();
ss.add(rs);
class Figure
{
public void add(Figure f){ System.out.println("Figure:add(Figure)"); }
}
class Rectangle extends Figure
{
@Override
public void add(Figure f){ System.out.println("Rectangle:add(Figure)"); }
public void add(Rectangle r){ System.out.println("Rectangle:add(Rectangle)"); }
}
class Square extends Rectangle
{
@Override
public void add(Figure f){ System.out.println("Square:add(Figure)"); }
public void add(Square s){ System.out.println("Square:add(Square)"); }
}
我在这里学到的是
基于此,前两个调用的结果符合预期。但是,我不理解示例3和4的结果。
它似乎是在Java语言规范中指定的,但我不理解。
但是,我不理解示例3和4的结果。
好吧,让我们单独看一下。
例子3
//Example 3 prints Rectangle:add(Rectangle). Expected Square:add(Square)
rs.add(new Square());
重要的部分是表达式和的 编译时 类型。rs``new Square()
rs
仅声明为Rectangle
,因此编译器将查看由声明的方法Rectangle
及其超类:
public void add(Figure f)
public void add(Rectangle r)
表达式的类型new Square()
是Square
,所以两种方法都 适用 -但第二种方法更 具体 。
因此,编译器将调用add(Rectangle)
所rs
引用的对象。就编译时而言就是这样。
在执行时,的值rs
引用-
的实例,Square
但Square
不会被覆盖,add(Rectangle)
因此选择的方法是中的实现Rectangle
:
public void add(Rectangle r){ System.out.println("Rectangle:add(Rectangle)"); }
例子4
//Example 4 prints Rectangle:add(Rectangle). Expected Square:add(Figure)
Square ss = new Square();
ss.add(rs);
再次,让我们考虑涉及的编译时类型… ss
是类型Square
,并且rs
是类型Rectangle
(编译时类型,请记住)。
由Square
及其超类声明的方法是:
public void add(Figure f)
public void add(Rectangle r)
public void add(Square s)
由于的编译时类型rs
仅为Rectangle
(not
Square
),因此前两种方法适用,而第三种方法则不适用。因此,再次add(Rectangle)
在编译时进行选择(因为它比更加具体add(Figure)
)。
同样,的执行时间类型ss
为Square
,不会覆盖add(Rectangle)
,因此使用中的实现Rectangle
。
让我知道这里是否有任何混淆-如果您可以具体说明哪个部分,那就太好了。
问题内容: 给出以下代码: 输出(意外)如下: 问题出在第一行,我希望与其他两行相同。此外,我发誓直到最近,编译器仍会为我的普通调用给出模糊的调用警告。但是,使用Java 5和6进行编译和测试会产生相同的结果。 这对我来说是一个重要的问题,因为我有很多代码都使用这种模式,即使用不同类型的重载“默认”参数来选择返回类型并推断所需的转换/解析。谁能解释这是怎么回事? 问题答案: Java始终以相同的方
Java 允许同一个类中定义多个同名方法,只要它们的形参列表不同即可。 如果同一个类中包含了两个或两个以上方法名相同的方法,但形参列表不同,这种情况被称为方法重载(overload)。 例如,在 JDK 的 java.io.PrintStream 中定义了十多个同名的 println() 方法。 这些方法完成的功能类似,都是格式化输出。根据参数的不同来区分它们,以进行不同的格式化处理和输出。它们之
问题内容: 示例代码为: 输出为: 的第一个调用会调用带有String参数的方法,根据可以理解。 1)谁能解释我在先前的调用中是基于什么来调用的? 2)再说一次,说一个if条件: 它总是使用 调用方法。 编译器在编译时会计算表达式吗?我想知道表达式是在编译时还是在运行时计算的。谢谢。 问题答案: Java使用早期绑定。在编译时选择最具体的方法。根据参数数量和参数类型选择最具体的方法。在这种情况下,
问题内容: 我正在尝试以下代码: 打印三遍。我希望方法选择要考虑实际的(而不是声明的)参数类型。我想念什么吗?是否有修改此代码,以便它会打印的方式, 问题答案: 我希望方法选择要考虑实际的(而不是声明的)参数类型。我想念什么吗? 是。您的期望是错误的。在Java中,动态方法分派仅发生在调用方法的对象上,而不发生在重载方法的参数类型上。 引用Java语言规范: 调用方法时(第15.12节),实际参数
问题内容: 谁能详细解释在我的测试代码段中使用实例时重载方法被调用的原因? 这里涉及虚拟方法或Java中方法重载/解析的特殊性吗?是否直接引用Java Lang Spec?哪个术语描述了这种行为?非常感谢。 问题答案: JLS在§8.4.9重载中规定: 调用方法时(第15.12节),实际参数(和任何显式类型参数)的数量以及参数的 编译时类型 在编译时用于确定将要调用的方法的签名( §15.12.2
问题内容: 在这里,输出是 字符串空 JVM为什么使用String参数将方法解析为一种? 在此先感谢J 问题答案: 这是一个相当复杂的算法,在JLS 15.12中进行了 详细介绍。但是这里相关的部分是15.12.2,它说“选择了最具体的部分”。对象和字符串重载都是“可访问且适用的”(字符串适用,因为空文字是所有类型的引用),并且字符串更具体。 编辑:根据语法更正的部分。