Java 为什么我们真的需要降级?
我想知道为什么我需要向下投射。我重新阅读了拼贴上的笔记,发现了下面的例子Java 为什么我们真的需要降级?,java,downcast,Java,Downcast,我想知道为什么我需要向下投射。我重新阅读了拼贴上的笔记,发现了下面的例子 class Student {...} class Graduate exteds Student { getResearchTopic(){...} // this method only exists in Graduate class. } 我们有一个学生类的参考,希望访问getResearchTopic方法 Student s1 = new Graduate(); if(s1 instanceof Gra
class Student {...}
class Graduate exteds Student {
getResearchTopic(){...} // this method only exists in Graduate class.
}
我们有一个学生类的参考,希望访问getResearchTopic方法
Student s1 = new Graduate();
if(s1 instanceof Graduate){
((Graduate)s1).getResearchTopic();
}
这是一个很好的向下投射的例子哈?我的问题是,为什么不首先宣布s1为毕业生?是否有一个现实生活中的例子,我将不得不向下转换而不是使用实际类的实例?好吧,您可以将引用
s1
声明为Graduate
类型。声明super-type
的引用所获得的主要好处是多态性的威力
使用指向子类对象的超类型引用,可以将同一引用绑定到多个子类对象。调用的实际方法将在运行时根据所指向的对象确定。但是,主要的条件是,该方法也应该在子类中定义,否则编译器将无法找到方法声明
在这里,您被迫downcast
,因为您还没有在超类中定义方法。因为编译器无法在Student
类中看到该方法的定义。它不知道实际对象s1
指向什么。请记住,编译器仅检查引用类型以查找meethod声明
通常,当您看到自己在代码中向下转换到子类时,几乎总是一个错误的信号(尽管也有一些例外)。你应该修改你的类
让我们看看使用超类引用而不是子类引用有什么好处:
Student student = new Phd();
student.getResearchTopic(); // Calls Phd class method
student = new Graduate();
student.getResearchTopic(); // Calls Graduate class method
例如:假设您有另一个子类Student
as:
class Phd extends Student {
getResearchTopic(){...}
}
您还可以在Student
类中提供一个定义(默认定义):
class Student {
getResearchTopic(){...}
}
现在,您创建以下两个对象,它们都由Student
reference指向:
Student student = new Phd();
student.getResearchTopic(); // Calls Phd class method
student = new Graduate();
student.getResearchTopic(); // Calls Graduate class method
因此,只需一个引用,就可以访问特定于子类的方法
您可以在
工厂方法模式中看到此功能的一个主要实现,其中单个静态方法根据某些条件返回不同子类的对象:
public static Student getInstance(String type) {
if (type.equals("graduate"))
return new Graduate();
else if (type.equals("phd"))
return new Phd();
}
因此,您可以看到相同的方法返回不同子类的对象
以上所有的事情你都可以做,仅仅因为一个概念:
超级类引用可以引用任何子类对象,但反之亦然。如果您想使用多态性,最好使用Student
对象,然后“downcast”使用特定于grade
对象的方法
通常,如果您有一个处理Student
对象的方法,那么该方法在编译时并不真正知道传入了什么类型的Student
对象。因此,在运行时,该方法应相应地检查特定类型和流程。假设您有一个方法,该方法将学生
作为参数。它所做的大多数事情对所有学生来说都是通用的。但是,如果它是一名毕业生
,那么它也可能会做其他事情。在这种情况下,您需要确定传入的学生是否实际上是毕业生
,并在该实例中执行一些特殊逻辑
也许是这样的:
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.
}
}
}
请注意,这样做通常是一种糟糕的编程实践,但有时是有意义的。当您试图创建泛型方法时,向下转换确实有帮助。例如,我经常看到将XML字符串
解析为对象
的代码。然后可以将对象
向下转换为您(作为编码者)知道它所代表的特定对象
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);
}
这让我可以创建一个非常通用的方法,在所有情况下都能实现我想要的功能。然后,当我调用它时,我可以简单地将对象
向下转换为我所知道的对象。它使我不必为每种对象类型创建单独的解析方法,并提高了代码的可读性
MyFirstClass parsedObject = (MyFirstClass) MyXMLTransformer.fromXML(xml);
使用默认Java反序列化程序反序列化对象时,使用此代码(使用其他反序列化程序时使用类似代码,例如Jackson JSON反序列化程序)
然后您需要将obj
转换为它的实际类型,因为readObject()
将始终返回一个普通的对象
-该方法无法静态验证正在读取的对象的类型为什么不首先将s1声明为毕业生这是main OP关心的问题,也许你想扩展答案来解决这部分问题。@LuiggiMendoza John。对不起,我刚才误读了这个问题。现在就编辑。事实上,使用向下投射会降低多态性的能力。@LuiggiMendoza。是的,对。我已经扩展了我的答案来解释一般概念。我想说,在这种情况下使用向下投射看起来像是应用程序中的一个设计问题。您必须使用向下转换的一个地方是重写equals
方法(如可能的dup问题中所示)。我正要发布一个答案=\无论如何花了太长时间。在您的情况下,您可以将该方法重新定义为private T fromXml(String xml,Class clazz)
,并使其成为真正的泛型方法,这样就避免了向下投射的问题。@LuiggiMendoza我想这也是真的。:)说得好。