使用Java抽象方法中继承的类来识别方法签名

7
我知道这是一个非常简单的问题,但我已经在Python上工作了相当长的时间,现在我必须回到Java,似乎我有问题理解Java基本的多态性。在Java中,使用继承类作为参数来覆盖(实现,准确地说)一个类的抽象方法是否可能?让我用一个非常简单的例子来解释一下(遵循形状的“几乎官方”example)。
class Shape {}
class Circle extends Shape {}
class Triangle extends Shape {}

abstract class ShapeDrawer {
    abstract void draw(Shape s); 
}                       
class CircleDrawer extends ShapeDrawer {
    void draw(Circle c){
        System.out.println("Drawing circle");
    }
}

有没有办法让Java将CircleDrawer类中的draw方法标识为ShapeDrawer中抽象方法draw的实现?(毕竟,Circle类是从Shape类继承而来的)

或者说:我想要的是CircleDrawer类中的draw方法仅接受Circle类型的实例,但同时,我想告诉Java编译器void draw(Circle c)实际上是其父类中abstract void draw(Shape s)方法的实现。

提前感谢您。


1
我可能只是读错了,但如果那不是你想要的,你能提供一个例子说明你想要什么吗? - Aify
1
在拔下芯片之前,不要忘记关闭电源。 - markspace
你有两个不同的ShapeDrawer类声明 - 一个是抽象类,一个不是。你想要哪个? - Mike Clark
请在发布代码之前检查编译错误,因为它们很多,而且很多东西都不清楚。 - user1803551
@MikeClark,不是的:我想要表明我不想要什么(作为澄清),但这显然比有帮助更加混乱。(我编辑了问题以删除它) - Savir
显示剩余2条评论
4个回答

8
您可以通过泛型解决您的问题:
public abstract class ShapeDrawer<T extends Shape> {
    public abstract void draw(T shape);
}

public class CircleDrawer extends ShapeDrawer<Circle> {
    public void draw(Circle circle) { ... }
}

4
你不能这样做,而且你不能这样做有很好的原因。以这个声明为例。
public abstract class ShapeDrawer {
    public abstract void draw(Shape s); 
}

现在看一下接收 ShapeDrawer 并尝试使用它的代码:

public void foo(ShapeDrawer drawer, Shape shape) {
    drawer.draw(shape);
}

这段代码应该可以正常工作,因为声明了 ShapeDrawer,承诺实现它的人会提供一个名为 draw() 的方法,并且该方法可以处理任何 Shape
但是如果你允许这样做:
public class CircleDrawer extends ShapeDrawer {
    public void draw(Circle c) {...}
}

如果这样说就不准确,你的CircleDrawer将无法满足它能处理任何Shape的承诺。


但是想象一下这个声明:

public abstract class ShapeCreator {
    public abstract Shape create();
}

public class CircleCreator extends ShapeCreator {
    public Circle create() {...}
}

这会有效吗?

是的,它会(只要您使用Java 5或更高版本),因为与第一个声明不同,ShapeCreator承诺将拥有一个名为create()的方法,该方法将返回一个Shape。由于CircleShapeShapeCreator的子类可以决定仅返回Circle,没有违反任何承诺。


那么如何实现您想要的效果呢?请参见loonytune的答案 :)


1
虽然技术上不行,但是你可以通过一些技巧来实现你所需的功能。
public abstract ShapeDrawer {
    public abstract void draw(Shape s); 
}                       
public CircleDrawer extends ShapeDrawer {
    public void draw(Shape s){
        if (s instanceof Circle) {
            System.out.println("Drawing circle");
        }
    }
}

这样做没问题,但本质上与已经给出的泛型示例相同。如果我要做这样的事情,我更喜欢使用泛型。 - markspace
它很接近,但不足以说它是完全相同的。通过这种方式,您仍然可以在技术上传递其他形状,并进行其他类型的处理-例如:打印“这不是一个圆形”之类的内容。但是通过使用泛型,您甚至不能使用不是圆形的东西调用它。 - Aify
在内部,所有的泛型都是测试实例类型,类似于您所做的。然而,编译器应该防止您传递任何不是圆形的东西,在大多数情况下这是一个巨大的胜利。 - markspace
大多数情况 - 就像我说的那样,这在技术上允许进行其他处理,而不仅仅是直接阻止它。我不知道为什么有人想要那样做,但我认为重点更在于它是一种选择,而并非比泛型方法更好的选择。@markspace - Aify

0
不可以使用子类型,否则会重载方法而不是覆盖它。Java方法签名必须完全匹配。
你可以返回一个子类型,但仅限于此,返回类型不是方法签名的一部分。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接