为什么我们将一个父引用分配给Java中的子对象?
我在问一个很简单的问题,但是我有点困惑。
假设我有一个class级Parent
:
public class Parent { int name; }
并有另一个类Child.java
:
public class Child extends Parent{ int salary; }
最后是我的Main.java类
public class Main { public static void main(String[] args) { Parent parent = new Child(); parent.name= "abcd"; } }
如果我做一个孩子的对象
Child child = new Child():
然后, child
对象可以访问name and salary
variables。
我的问题是:
Parent parent = new Child();
只给出Parent类的name
variables的访问权限。 那么这条线的确切用途是什么?
Parent parent = new Child();
而且当它使用dynamic多态时,为什么在这样做后子类的variables是不可访问的
Parent parent = new Child();
首先,澄清术语:我们将一个Child
对象分配给一个Parent
types的variables。 Parent
是一个对象的引用,恰好是Parent
一个子types。
它只是在一个更复杂的例子中有用。 想象一下,将getEmployeeDetails
添加到类Parent:
public String getEmployeeDetails() { return "Name: " + name; }
我们可以在Child
覆盖该方法来提供更多的细节:
@Override public String getEmployeeDetails() { return "Name: " + name + " Salary: " + salary; }
现在,您可以编写一行代码,获取任何可用的详细信息,无论该对象是Parent
还是Child
:
parent.getEmployeeDetails();
以下代码:
Parent parent = new Parent(); parent.name = 1; Child child = new Child(); child.name = 2; child.salary = 2000; Parent[] employees = new Parent[] { parent, child }; for (Parent employee : employees) { employee.getEmployeeDetails(); }
将导致输出:
Name: 1 Name: 2 Salary: 2000
我们用一个Child
作为Parent
。 它具有对于Child
类独特的专门行为,但是当我们调用getEmployeeDetails()
我们可以忽略差异,并重点关注Parent
和Child
是如何相似的。 这被称为亚型多态性 。
您更新后的问题会询问为什么Child.salary
在Child
对象存储在Parent
引用中时不可访问。 答案是“多态”和“静态types”的交集。 由于Java在编译时是静态types的,所以你可以从编译器得到一定的保证,但是你不得不遵循交换规则或者代码不能编译。 在这里,相关的保证是子types(例如Child
)的每个实例都可以用作其超types的一个实例(例如Parent
)。 例如,您可以保证,当您访问employee.getEmployeeDetails
或employee.name
,方法或字段将定义在任何可以分配给Parent
types的variablesemployee
的非null对象上。 为了保证这一点,编译器在决定你可以访问什么的时候只考虑那个静态types(基本上是variables引用的types, Parent
)。 因此,您无法访问在对象的运行时typesChild
上定义的任何成员。
当你真的想要使用一个Child
作为Parent
这是一个容易的限制住,你的代码将可用于Parent
及其所有子types。 当不能接受的时候,制作参考Child
的types。
它允许你通过一个公共的父接口访问所有的子类。 这对于运行所有子类上可用的通用操作是有益的。 需要一个更好的例子:
public class Shape { private int x, y; public void draw(); } public class Rectangle extends Shape { public void draw(); public void doRectangleAction(); }
现在如果你有:
List<Shape> myShapes = new ArrayList<Shape>();
您可以将列表中的每个项目都引用为一个Shape,您不必担心它是否为Rectangle或其他types,比如说Circle。 你可以一视同仁 你可以把他们全部画出来。 你不能调用doRectangleAction,因为你不知道Shape是否真的是一个矩形。
这是一种以通用方式处理对象和专门处理对象之间的交易。
真的,我认为你需要阅读更多关于OOP。 一本好书应该帮助: http : //www.amazon.com/Design-Patterns-Explained-Perspective-Object-Oriented/dp/0201715945
如果将父types分配给子类,则意味着您同意使用父类的公共特征。
它使您可以自由地从不同的子类实现中进行抽象。 结果限制了你的父function。
然而,这种types的任务被称为上传。
Parent parent = new Child();
相反是倒下。
Child child = (Child)parent;
因此,如果您创buildChild
实例并将其向下转换为Parent
,则可以使用该types的属性name
。 如果您创buildParent
实例,则可以执行与之前的情况相同的操作,但不能使用salary
因为Parent
没有这样的属性。 返回到可以使用salary
的前一个案例,但只有在向下转换为Child
。
有更详细的解释
在编译程序时,基类的引用variables会获取内存,编译器会检查该类中的所有方法。 所以它检查所有的基类方法,而不是子类方法。 现在,在运行时创build对象时,只能运行已检查的方法。 在函数运行的子类中覆盖函数的情况。 Child class其他函数不运行coz编译器在编译时没有识别它们。
假设你想拥有一个Parent类实例的数组,并且有一组子类Child1,Child2,Child3可以扩展Parent。 有时你只对父类实现感兴趣,而这个实现更通用,并且不关心子类引入的更具体的东西 。
这种情况发生在你有几个实现。 让我解释。 假设你有几种sortingalgorithm,你想在运行时select一个实现,或者你想给别人添加他的实现的能力。 为了解决这个问题,你通常创build一个抽象类(Parent)并且有不同的实现(Child)。 如果你写:
Child c = new Child();
你将你的实现绑定到Child类,你不能再改变它。 否则,如果你使用:
Parent p = new Child();
只要Child扩展Parent,您就可以在将来修改代码。
同样的事情可以使用接口完成:父不再是一个类,而是一个Java接口。
一般来说,您可以在DAO模式中使用这种方式,在这种方式中,您希望有几个与数据库相关的实现。 你可以看看FactoryPatter或AbstractFactory模式。 希望这可以帮到你。
这很简单。
Parent parent = new Child();
在这种情况下,对象的types是Parent
。 antParent
只有一个属性。 这是name
Child child = new Child();
在这种情况下,对象的types是Child
。 antChild
有两个属性。 他们是name
和salary
。
事实是,在声明中不需要立即初始化非final字段。 通常这是在运行时完成的,因为通常你不知道到底需要什么实现。 举个例子,假设你有一个头部类Transport
的类层次结构。 还有三个小类: Car
, Helicopter
和Boat
。 还有另外一个有现场Transport
课程。 那是:
class Tour { Transport transport; }
只要用户没有预定旅程,也没有select特定types的交通工具,则无法初始化该字段。 这是第一个。
其次,假设所有这些类都必须有一个方法go()
但是具有不同的实现。 您可以在超类Transport
默认定义一个基本实现,并在每个子类中拥有唯一的实现。 通过这个初始化Transport tran; tran = new Car();
Transport tran; tran = new Car();
你可以调用tran.go()
方法并得到结果,而不用担心具体的实现。 它会从特定的子类调用overrided方法。
而且你可以在任何使用超类的实例的地方使用子类的实例。 例如,你想提供机会租用你的交通工具。 如果你不使用多态,你必须为每个案例写很多方法: rentCar(Car car)
, rentBoat(Boat boat)
等等。 同时多态可以让你创build一个通用的rent(Transport transport)
。 你可以传入任何Transport
子类的对象。 另外,如果随着时间的推移,你的逻辑会增加,你需要在层次结构中创build另一个类。 当使用多态时,你不需要改变任何东西。 只需扩展类Transport
然后将新类传入方法:
public class Airplane extends Transport { //implementation }
和rent(new Airplane())
。 和new Airplane().go()
第二种情况。
你声明parent为Parent,所以java将只提供Parent类的方法和属性。
Child child = new Child();
应该pipe用。 要么
Parent child = new Child(); ((Child)child).salary = 1;