为什么我们真的需要向下倾斜?

时间:2013-07-16 19:09:50

标签: java downcast

我想知道为什么我需要Downcasting。我从拼贴中重读了我的笔记并找到了下面的例子。

class Student {...}

class Graduate exteds Student {
   getResearchTopic(){...} // this method only exists in Graduate class.
}

我们有一个ref类到Student类,想要访问getResearchTopic方法;

Student s1 = new Graduate();
if(s1 instanceof Graduate){
   ((Graduate)s1).getResearchTopic();
}

向下倾斜的好例子哈?我的问题是为什么不首先宣布s1为毕业生?是否有一个真实的例子,我将不得不转发而不是使用实际类的实例?

5 个答案:

答案 0 :(得分:6)

假设您有一个以Student为参数的方法。它所做的大多数事情对所有学生来说都是通用的。但如果它是Graduate,那么它可能还有其他功能。在这种情况下,您需要确定传入的Student是否实际上是Graduate并在该实例中执行某些特殊逻辑。

也许是这样的:

class StudentDAO {
    public void SaveStudent(Student s) {
       // Do something to save the student data to a database.

       if ( s instanceof Graduate ) {
           // Save their research topic too.
       }
    }
}

请注意,做这种事情通常是一种糟糕的编程习惯,但有时它是有道理的。

答案 1 :(得分:5)

好吧,您可以将引用s1声明为Graduate类型。通过声明super type的引用获得的主要好处是多态性的强大功能。

使用指向子类对象的超类型引用,可以将相同的引用绑定到多个子类对象。调用的实际方法将在运行时根据指向的对象来决定。但是,主要条件是,该方法也应该在子类中定义,否则编译器将无法找到方法声明。

在这里,你被迫downcast,因为你没有在超类中定义方法。由于编译器无法在Student类中看到该方法的定义。它不知道实际对象s1指向的是什么。请记住,编译器只检查引用类型以查找meethod声明。

一般情况下,每当你看到自己向代码中的子类转发时,它几乎总是一个错误的标志(虽然有一些例外)。你应该修改你的课程。


让我们看看使用超类引用而不是子类引用可以获得什么好处:

例如:假设您有另一个Student子类:

class Phd extends Student {
    getResearchTopic(){...}
}

并且您还在Student类中提供了一个定义(默认值):

class Student {
    getResearchTopic(){...}
}

现在,您创建了以下两个对象,两者都由Student引用指向:

Student student = new Phd();
student.getResearchTopic();   // Calls Phd class method

student = new Graduate();
student.getResearchTopic();    // Calls Graduate class method

因此,只需一个引用,就可以访问特定于子类的方法。


您可以在factory method模式中看到此功能的一个主要实现,其中单个静态方法根据某些条件返回不同子类的对象:

public static Student getInstance(String type) {
    if (type.equals("graduate")) 
        return new Graduate();
    else if (type.equals("phd"))
        return new Phd();
}

因此,您可以看到同一个方法返回不同子类的对象。

你可以做的所有上述事情只是因为一个概念:

超类引用可以引用任何子类对象,但反之亦然

答案 2 :(得分:1)

使用默认Java反序列化器反序列化对象时,使用此代码(使用其他反序列化器时使用类似代码,例如Jackson JSON反序列化器)

ObjectInputStream ois = new ObjectInputStream(in);
Object obj = ois.readObject();

然后,您需要将obj强制转换为其实际类型,因为readObject()将始终返回普通的旧Object - 该方法无法静态验证正在读取的对象类型

答案 3 :(得分:0)

如果您想使用多态,最好使用Student个对象,然后“向下转换”以使用特定于Graduate个对象的方法。

通常,如果您有一个与Student个对象一起使用的方法,那么该方法在编译时并不真正知道传入的Student个特定类型的对象。因此,在运行时-time,该方法应相应地检查特定的类型和过程。

答案 4 :(得分:0)

当您尝试制作通用方法时,向下转换会有所帮助。例如,我经常看到将XML String解析为Object的代码。然后,Object可以向下转换为您(作为编码员)知道它代表的特定Object

private static XStream xstream = new XStream(new DomDriver());
static {
    xstream.processAnnotations(MyFirstClass.class);
    xstream.processAnnotations(MySecondClass.class);
    // ...
}

public static Object fromXML(String xml) {
    return xstream.fromXML(xml);
}

这让我可以创建一个非常通用的方法来完成我希望它在所有情况下执行的操作。然后,当我打电话给它时,我可以简单地将Object转换成我所知道的。它阻止我必须为每个对象类型创建一个单独的解析方法,并提高我的代码的可读性。

MyFirstClass parsedObject = (MyFirstClass) MyXMLTransformer.fromXML(xml);