Java重载方法选择
我试图了解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
,因此两种方法都适用 – 但第二种方法更具体 。
因此编译器将在rs
引用的对象上调用add(Rectangle)
。 这就是编译时的一面。
在执行时, 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
(非Square
),前两种方法适用,但第三种方法不适用。 因此,再次在编译时选择add(Rectangle)
(因为它比add(Figure)
更具体)。
同样, ss
的执行时间类型是Square
,它不会覆盖add(Rectangle)
,因此使用Rectangle
的实现。
让我知道这里的任何事情是否令人困惑 – 如果你能具体说明哪个部分,那就太棒了。
rs.add(new Square());
声明的rs类型是Rectangle。 因此,它查看Rectangle中的方法以及所有采用Square或与Square兼容的类型作为参数的超类。 最具体的一个是add(Rectangle)
因为Square是一个Rectangle,因为Rectangle比Figure更具体。
Square ss = new Square(); ss.add(rs);
在Square和所有超类中查找方法add(Rectangle)
。 Rectangle.add(Rectangle)
,因为Square.add(Square)
不适用(Rectangle不是Square),而Square.add(Figure)
不太具体。