对象toString方法及里氏代换原则

Object toString method and Liskov substitution principle

每个 class 都直接或间接地继承自 Object class。

Object class 等具有重要的方法,最常被重写:toString.

问题是:重写此方法是否会违反关于 Object class 的里氏替换原则?

我来举个例子。

public class Main
{
    public static void main(String[] args)
    {
        Object o = new Object();
        String s = o.toString();
        if (s.indexOf('@') > -1) {
            System.out.println("OK");
        } else {
            System.out.println(":-(");
        }
    }
}

public class MyClass
{
    private int x;

    public string toString()
    {
        return Integer.toString(x);
    }
}

显然,如果我将 new Object() 替换为 new MyClass(),系统的行为就会发生变化。

好吧,这是一个品味问题。 Object几乎没有保证属性。所以也没有什么可以违反的。

如果说返回class名字这样一个可能违规的属性,那当然是一个subclass 不应该改变这一点。但是看了Object.toString()的文档发现并没有这样的保证:

Returns a string representation of the object. In general, the toString method returns a string that "textually represents" this object.

所以我在这里没有看到 LSP 违规

LSP 并没有说子class 的行为必须与超级class 完全相同。这将使 subclasses 完全无用。只要求subclasses 满足superclass.

的规范

唯一可以提及的是 Object 对每个对象 执行了一个无意义的方法 toString .更复杂的设计可能会将其放入界面中。

我认为这种设计选择只是一种妥协,也被 .NET 等其他语言所取代。

里氏代换原则只要求接口兼容。它没有说明任何有关潜在行为的信息。例如

public interface Text {
    String value();
}

public class SimpleText implements Text {

    private final String value;

    public SimpleText(String value) {
        this.value = value;
    }

    @Override
    public String value() {
        return this.value;
    }
}

public class NumberText implements Text {

    private final Number number;

    public NumberText(Number number) {
        this.number = number;
    }

    @Override
    public String value() {
        return String.format("%.3f", this.number.doubleValue());
    }
}

你不关心实现细节,可以这样交流:

//We care only about value() method, not its specific implementation
Text text = new SimpleText("text");
text.value();
text = new NumberText(44);
text.value();

请注意,如果您的实现将抛出任何异常,那么它将违反 LSP,因为 Object.toString() 不会抛出任何异常。