我在(Java)面向对象编程讲座中遇到了Liskov替换原理和演员。我明白了这个原则是什么,也就是说,我可以用超类的类型初始化一个子类:
SuperClass superClass = new SubClass();
我首先关注的是此类操作的目的。为什么我不能像往常一样声明子类(示例如下)?
SubClass subClass = new SubClass();
在此之后,我被迫坚持施法,如下:
SuperClass superClass = new SubClass();
SubClass subClass = (SubClass)superClass;
同样,我很难理解所有这一切。
任何人都可以就这些程序的目的作出任何澄清吗?
答案 0 :(得分:1)
Liskov替换原则意味着子类应该在理解您只应该真正关心实例化实例的子类时完成。从那时起,它应该被视为超级类,没有额外的方法或特殊处理。
这种方法的最大优点是,如果你决定要用另一个子类替换一个子类,你可以!它的调用方式与前一个子类的调用方式完全相同。如果这对你的新子类来说不方便,那么Liskov会说这意味着它不应该真正成为那个超类的子类。应该不加选择地对待子类。
关于你的转换,当你将SubClass分配给SuperClass时,你现在有了一个可以在整个程序中使用的实例,而不必特别关注它是SubClass。您尝试将superClass实例强制转换为SubClass 的以下行可以完成,但您可能没有正确执行某些操作(同样,因为您不应该&# 39;关心它是什么子类)。
通常,您会看到工厂或方法将其实例化如下:
public SuperClass getSuperClass() {
SubClass subClass = new SubClass();
// Here is the only place where you should perform subclass specific
// calls. The less you need to do here, the better.
subClass.setSubClassProperty(this);
return subClass;
}
注意隐式转换为SuperClass,因为我返回一个SubClass实例,并且调用者被赋予一个SuperClass实例。这就是我们喜欢它的方式。调用者可以接收一个SubClass实例,但我们不希望调用者使用SubClass方法的诱惑。
答案 1 :(得分:0)
因为这样你可以使用超类方法而不需要了解子类的任何内容。考虑下一个例子:
想象一下可以得出的东西:
public abstract class Thing {
public abstract void draw();
}
如果您有这样的事情列表,您可以循环绘制它们:
List<Thing> list = ...
for(thing: list) {
thing.draw()
}
现在你要绘制实际的东西:圆形,正方形等。所以你要写具体的类:
public class Circle extends Thing {
private final Point center;
public Circle(Point center) {
this.center = center;
}
public void draw() {
//draw a circle in coordinates center
}
}
public class Square...
如何创建具体事项列表?很容易由于超类到子类的概念:
Thing circle1 = new Circle(10,10);
Thing square = new Square(0,0,10,10);
Thing circle2 = new Circle(50,100);
现在将它们添加到列表中并在同一循环中使用。
List<Thing> list = new ArrayList<>();
list.addAll(square, circel1, circle2);
for( thing: list) {
thing.draw()
}