Nar*_*Pal 58 java oop inheritance casting upcasting
我问的是一个非常简单的问题,但我对此感到困惑.
假设我有一个班级Parent
:
public class Parent {
int name;
}
Run Code Online (Sandbox Code Playgroud)
并有另一堂课Child.java
:
public class Child extends Parent{
int salary;
}
Run Code Online (Sandbox Code Playgroud)
最后是我的Main.java类
public class Main {
public static void main(String[] args)
{
Parent parent = new Child();
parent.name= "abcd";
}
}
Run Code Online (Sandbox Code Playgroud)
如果我做一个像儿童的对象
Child child = new Child():
Run Code Online (Sandbox Code Playgroud)
然后child
对象可以访问这两个name and salary
变量.
我的问题是:
Parent parent = new Child();
Run Code Online (Sandbox Code Playgroud)
只name
提供父类变量的访问权限.那么这条线的确切用途是什么?
Parent parent = new Child();
Run Code Online (Sandbox Code Playgroud)
而且当它使用动态多态时,为什么在执行此操作后无法访问子类的变量
Parent parent = new Child();
Run Code Online (Sandbox Code Playgroud)
Joh*_*tts 41
首先,澄清术语:我们将一个Child
对象分配给一个类型的变量Parent
.Parent
是正好是的子类型的对象的引用Parent
,一个Child
.
它仅适用于更复杂的示例.想象一下,你添加getEmployeeDetails
到类Parent:
public String getEmployeeDetails() {
return "Name: " + name;
}
Run Code Online (Sandbox Code Playgroud)
我们可以覆盖该方法Child
以提供更多详细信息:
@Override
public String getEmployeeDetails() {
return "Name: " + name + " Salary: " + salary;
}
Run Code Online (Sandbox Code Playgroud)
现在,您可以编写代码,获取任何信息可用,对象是一条线Parent
或Child
:
parent.getEmployeeDetails();
Run Code Online (Sandbox Code Playgroud)
以下代码:
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();
}
Run Code Online (Sandbox Code Playgroud)
将导致输出:
Name: 1
Name: 2 Salary: 2000
Run Code Online (Sandbox Code Playgroud)
我们用a Child
作为Parent
.它具有Child
该类独有的专门行为,但是当我们打电话时,getEmployeeDetails()
我们可以忽略差异,并专注于如何Parent
和Child
相似.这称为亚型多态性.
您更新的问题询问Child.salary
当Child
对象存储在Parent
引用中时无法访问的原因.答案是"多态"和"静态类型"的交集.因为Java在编译时是静态类型的,所以您可以从编译器获得某些保证,但是您必须在交换中遵循规则,否则代码将无法编译.这里,相关保证是子类型(例如Child
)的每个实例都可以用作其超类型的实例(例如Parent
).例如,您可以保证在访问时,employee.getEmployeeDetails
或者employee.name
在可以分配给employee
类型变量的任何非null对象上定义方法或字段Parent
.为了保证这一点,编译器Parent
在决定您可以访问的内容时只考虑该静态类型(基本上是变量引用的类型).因此,您无法访问在对象的运行时类型上定义的任何成员,Child
.
当你真正想要使用a Child
作为一个Parent
这是一个容易的限制,你的代码将可用Parent
于其所有子类型.如果不可接受,请输入参考类型Child
.
ani*_*nio 10
它允许您通过公共父接口访问所有子类.这有利于运行所有子类上可用的常见操作.需要一个更好的例子:
public class Shape
{
private int x, y;
public void draw();
}
public class Rectangle extends Shape
{
public void draw();
public void doRectangleAction();
}
Run Code Online (Sandbox Code Playgroud)
现在,如果你有:
List<Shape> myShapes = new ArrayList<Shape>();
Run Code Online (Sandbox Code Playgroud)
您可以将列表中的每个项目作为Shape引用,您不必担心它是Rectangle还是其他类型,比如说Circle.你可以对待它们; 你可以画出所有这些.你不能调用doRectangleAction,因为你不知道Shape是否真的是一个矩形.
这是您以通用方式处理对象和特定处理对象之间的交易.
真的,我认为你需要阅读更多有关OOP的信息.一本好书应该有所帮助:http://www.amazon.com/Design-Patterns-Explained-Perspective-Object-Oriented/dp/0201715945
如果将父类型分配给子类,则表示您同意使用父类的公共功能.
它使您可以自由地从不同的子类实现中进行抽象.因此,您可以使用父功能限制您.
但是,这种类型的赋值称为upcasting.
Parent parent = new Child();
Run Code Online (Sandbox Code Playgroud)
相反的是向下倾斜.
Child child = (Child)parent;
Run Code Online (Sandbox Code Playgroud)
因此,如果您创建实例Child
并向下投影,则Parent
可以使用该类型属性name
.如果你创建的实例Parent
你可以像以前的情况一样做但你不能使用,salary
因为在那里没有这样的属性Parent
.返回上一个可以使用salary
但只有向下转换的情况Child
.
这很简单。
Parent parent = new Child();
Run Code Online (Sandbox Code Playgroud)
在这种情况下,对象的类型为Parent
。蚂蚁Parent
只有一个属性。是name
。
Child child = new Child();
Run Code Online (Sandbox Code Playgroud)
在这种情况下,对象的类型为Child
。蚂蚁Child
有两个属性。他们是name
和 salary
。
事实是,不需要在声明时立即初始化非最终字段。通常,这是在运行时完成的,因为通常您无法确切知道所需的实现。例如,假设您拥有一个以类Transport
为首的类层次结构。和三个子类:Car
,Helicopter
和Boat
。还有另一种Tour
具有field 的类Transport
。那是:
class Tour {
Transport transport;
}
Run Code Online (Sandbox Code Playgroud)
只要用户尚未预订行程且未选择特定的交通工具,就无法初始化此字段。第一次
其次,假设所有这些类都必须具有一个方法,go()
但是具有不同的实现。您可以默认在超类中定义一个基本实现,Transport
并在每个子类中拥有自己的唯一实现。通过这种初始化,Transport tran; tran = new Car();
您可以调用该方法tran.go()
并获得结果,而不必担心特定的实现。它将从特定的子类调用重写的方法。
此外,您可以在使用超类实例的任何地方使用子类实例。例如,您想提供租用交通工具的机会。如果不使用多态,则必须为每种情况编写很多方法:rentCar(Car car)
,rentBoat(Boat boat)
依此类推。同时,多态允许您创建一种通用方法rent(Transport transport)
。您可以传入的任何子类的it对象Transport
。另外,如果随着时间的流逝,您的逻辑会增加,并且您需要在层次结构中创建另一个类?使用多态时,您无需更改任何内容。只需扩展类Transport
并将新类传递给方法即可:
public class Airplane extends Transport {
//implementation
}
Run Code Online (Sandbox Code Playgroud)
和rent(new Airplane())
。而new Airplane().go()
在第二种情况下。
编译程序时,基类的引用变量获取内存,编译器检查该类中的所有方法.因此它检查所有基类方法,但不检查子类方法.现在,在运行时创建对象时,只能运行已检查的方法.如果在子类中重写了该函数运行的方法.子类其他函数未运行,因为编译器在编译时未识别它们.
我知道这是一个非常古老的线程,但我曾经遇到过同样的疑问。
所以这个概念和Parent parent = new Child();
java中的早期绑定和后期绑定有关。
私有、静态和最终方法的绑定发生在编译时,因为它们不能被重写,普通方法调用和重载方法是早期绑定的示例。
考虑这个例子:
class Vehicle
{
int value = 100;
void start() {
System.out.println("Vehicle Started");
}
static void stop() {
System.out.println("Vehicle Stopped");
}
}
class Car extends Vehicle {
int value = 1000;
@Override
void start() {
System.out.println("Car Started");
}
static void stop() {
System.out.println("Car Stopped");
}
public static void main(String args[]) {
// Car extends Vehicle
Vehicle vehicle = new Car();
System.out.println(vehicle.value);
vehicle.start();
vehicle.stop();
}
}
Run Code Online (Sandbox Code Playgroud)
输出:100
汽车启动
车辆停止
发生这种情况是因为stop()
它是静态方法并且无法被覆盖。因此,绑定stop()
发生在编译时,并且start()
是非静态的,在子类中被重写。因此,有关对象类型的信息仅在运行时可用(后期绑定),因此start()
调用 Car 类的方法。
同样在这段代码中,vehicle.value
我们得到了100
输出,因为变量初始化不属于后期绑定。方法重写是java支持运行时多态性的方式之一。
我希望这能回答Parent parent = new Child();
重要的地方以及为什么您无法使用上述参考访问子类变量。