对象转型是一种好的实践吗?

11

面向对象编程语言,例如Java、C#等,支持对象类型转换。例如在Java中,以下代码是合法的:

URL url = new URL("url");    
URLConnection conn = url.openConnection();

if( !conn instanceof HttpURLConnection )
  throw new Exception("not http request");

HttpURLConnection con = (HttpURLConnection) conn;   

或者是我尝试过的另一个基本示例:

public class Base
{
  public void base(){}
}

public class Derived extends Base
{
  public void derived(){}
}

Base b = new Derived();
b.base();

派生类拥有基类所有方法,再加上更多方法。你完全可以通过调用派生类构造函数来创建基类。

我还看到了这个链接 http://www.volantec.biz/castingObjects.htm,它解释了对象类型转换的内部工作原理。到目前为止,没问题。

但是为什么第一个例子不使用 HttpURLConnection con = new HttpURLConnection("url address")(我知道 HttpURLConnection 是一个抽象类)。那样看起来更清晰、简单。另一方面,当你处理接口时,对象类型转换很方便。另一个例子是List<Object>列表,在某些类中我有时会看到它。它意味着你可以在此列表中保存任何可能的类。之后,如果你知道它的类型,就可以将其强制转换回原始类型。那么只将特定类存储到列表中,如List<String>List<MyClass>,这样是否更清晰些?那么使用List<Object>是好的设计实践吗?


2
强制转换为HttpURLConnection是不良面向对象设计的典型例子。关于通用列表:这取决于情况,但我宁愿选择尽可能参数化的列表,这样我就可以在编译时捕获许多运行时类型转换异常。 - Lyubomyr Shaydariv
3
答案已经包含在问题中:HttpUrlConnection是一个抽象类,因此您无法创建HttpUrlConnection的实例,只能创建它的子类实例。这就是url.openConnection()的作用。 - JB Nizet
@JBNizet 我在括号里提到了这点。我知道HttpURLConnection是一个抽象类,因此不能像你所说的那样创建实例。你说得对,我只是想演示一下它,也许其他的例子更合适。 - broadband
2个回答

10
在设计类层次结构时,您必须始终牢记Liskov替换原则(LSP)

派生类型必须完全可替代其基类型。

换句话说,要决定是否扩展类,您应该问自己,如果将依赖于新类的组件更改为其基类,是否能够很好地为其服务。
转换的问题在于,如果您需要将Base类对象转换为Derived类对象,则意味着您正在违反LSP。
当您期望接口时需要确保对象来自特定的实现时,那么您的设计肯定存在问题。
接口就像合同。如果您正在使用接口中没有的实现方法,则意味着您正在违反合同,并在代码和实现之间创建耦合。
请记住,您的代码应始终依赖于抽象而不是具体的实现,这是依赖倒置原则

如果有一个车辆类作为TwoWheeler和FourWheeler类的基类,并且在其他类中我们创建了一个List<Vehicle>,可以将两个子类实例添加为元素。那么,如果我们需要访问子类属性并在那里使用类型转换,这是否会是一个不好的设计呢?请回答。 - VR7

0
通过使用Object类进行强制转换,您基本上为派生类提供了所有可用的权限,即函数永远不会拒绝即将到来的参数,因为您已经提供了可以访问该对象的Object,这是危险的。正如@Henrique所说,如果您需要确保对象是特定实现而不是接口时,则设计肯定存在问题。

3
请将英语翻译为中文。仅返回已翻译的文本:请避免使用英语的缩写形式。 - lalithkumar
Api是由Java设计的 - https://docs.oracle.com/javase/7/docs/api/java/net/HttpURLConnection.html。 - broadband
请提及“API是由JAVA设计”的完整参考资料。 - Siddharth Choudhary

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