当前位置: 首页 > 知识库问答 >
问题:

试图理解Liskov代换原理

杜思远
2023-03-14

我试图理解Liskov替换原理,我有以下代码:

class Vehicle {
}

class VehicleWithDoors extends Vehicle {
    public void openDoor () {
        System.out.println("Doors opened.");
    }
}

class Car extends VehicleWithDoors {
}

class Scooter extends Vehicle {
}

class Liskov {
    public static void function(VehicleWithDoors vehicle) {
        vehicle.openDoor();
    }

    public static void main(String[] args) {
        Car car = new Car();
        function(car);
        Scooter scooter = new Scooter();
        //function(scooter);  --> compile error
    }
}

我不确定这是否违反了它。原理是,如果你有一个类S的对象,那么你可以用另一个类T的对象来代替它,其中S是T的一个子类。但是,如果我写了

Vehicle vehicle = new Vehicle();
function(vehicle);

这当然会产生编译错误,因为Vehicle类没有openDoor()方法。但这意味着我不能用它们的父类Vehicle替换VehicleWithDoors对象,这似乎违反了原则。那么这个代码是否违反了它?我需要一个好的解释,因为我似乎不明白。

共有2个答案

沈国安
2023-03-14

扩展类或接口时,新类仍然是它扩展的类型。对此进行推理的最简单方法(IMO)是将子类视为超类的一种特殊类型。所以它仍然是超类的一个实例,还有一些额外的行为。

例如,带车门的车辆仍然是车辆,但它也有车门。滑板车也是一种交通工具,但它没有门。如果您有打开车门的方法,则车辆必须有车门(因此,当您将踏板车传递给它时会出现编译时错误)。同样,对于接受某个类的对象的方法,您可以传递作为其子类实例的对象,该方法仍然可以工作。

就实现而言,您可以安全地将任何对象强制转换为其超类型之一(例如,汽车和滑板车车辆汽车带门车辆),但不能反过来(如果您进行一些检查并明确地强制转换,则可以安全地执行此操作)。

潘志国
2023-03-14

你倒过来了。该原则规定,“如果ST的子类型,则程序中T类型的对象可以替换为S类型的对象,而不改变该程序的任何期望属性”。

基本上,车辆应该在车辆工作的地方工作。这显然并不意味着车辆应该在车辆与门工作的地方工作。换句话说,您应该能够在不影响程序正确性的情况下用专门化替换泛化。

示例冲突是一个ImmutableList扩展一个定义add操作的List,其中不可变实现抛出异常。

js lang-js prettyprint-override">class List {
  constructor() {
    this._items = [];
  }
  
  add(item) {
    this._items.push(item);
  }
  
  itemAt(index) {
    return this._items[index];
  }
}

class ImmutableList extends List {
  constructor() {
    super();
  }
  
  add(item) {
    throw new Error("Can't add items to an immutable list.");
  }
}
 类似资料:
  • 我试图通过反复阅读维基百科条目来确定我对上述原则的理解。 撇开仍然让我悲伤的协变和逆变的概念不谈,wikipedia还提到超类型的不变量必须保留在子类型和历史约束或历史规则中。基于最后两个概念,我提出了一个小例子: 所以我的问题是:基于上述两个概念,我用这个例子是否违反了原则?若否,原因为何? 事先非常感谢。

  • 存在无法写入或查找的流派生类这一事实是否违反了Liskov替换原则? 例如,无法查找NetworkStream,如果调用方法,它将抛出。 还是因为存在标志就可以了? 考虑到众所周知的继承自的例子...将标志和添加到是否可以解决问题? 这难道不是打开了通过添加旗帜来解决问题的大门吗?

  • 我在派生类中重写了带有附加先决条件的虚拟函数。这是快照- 如果我理解正确的话,这里的代码通过附加一个先决条件打破了Liskov替换-IsImmediateProcess和其他日期检查。对吗?或者一个被重写的函数调用一个基函数,然后向它添加自己的行为,这样可以吗? 我不能将重写方法中由初始过程类型引入的条件移动到基本类型,因为它是特定于初始过程的。 在这种情况下,如果派生类重写行为并希望在不违反Li

  • LSP定义指出,如果S是T的子类型,则程序中T类型的对象可以替换为S类型的对象,而不改变该程序的任何期望属性。 子类型中的前提条件不能加强 例如,我有下面的类,这是违反(在子类型中不能加强前提条件)。我正试图把我的头绕在这上面,请有人提供一个好的例子来理解它。

  • 来自维基百科, Liskov的行为子类型概念定义了对象的可替代性概念;也就是说,如果S是T的子类型,则程序中T类型的对象可以替换为S类型的对象,而不改变该程序的任何期望属性(例如正确性)。 假设以下类层次结构: 基本抽象类-。它有一个只读属性,在后继程序中被重写。 基类的继承者-,它重写并返回灰色。 Cat的继任者-,它覆盖并返回带条纹的。 然后我们声明一个方法,参数类型为(不是)。 向该方法发送