以下是在Java中创建对象的常用语法:
Puppy myPuppy = new Puppy( "tommy" );
根据TutorialsPoint上Java类的this description,此语法的组成部分如下:
声明-具有对象类型的变量名称的变量声明。
实例化-'new'关键字用于创建对象。
初始化-在'new'关键字之后是对构造函数的调用。该调用将初始化新对象。
根据this Quora article,构造函数必须与其所属的类具有相同的名称。
但是,有时我会看到在语法的构造函数部分与类名不同的地方创建了Java对象,如下所示:
Dog myDog = new Puppy( "tommy" );
我不明白上述语法如何有效。到底在做什么myPuppy是哪种类型的课程?
编辑:抱歉-为了使这一点更清楚,我在最后一行代码中翻转了Dog / Puppy引用,因为Puppy类扩展Dog类具有更大的逻辑意义。 / p>
答案 0 :(得分:1)
如果Dog是Puppy的子类,或者Puppy是Interface,而Dog是其实现,则这是可能的。这是Java中多态的经典示例。
答案 1 :(得分:1)
我们在这里谈论两件事:
1)创建一个包含类定义的文件:
MyClass.java
public class MyClass {
private int someValue;
public MyClass(int someValue) {
this.someValue = someValue;
}
}
2)创建一个扩展其他类或实现接口的类:
MyInterface.java
public interface MyInterface {
void doSomeAction();
}
MyClass.java
public class MyClass implements MyInterface {
private int someValue;
public MyClass(int someValue) {
this.someValue = someValue;
}
public void doSomeAction() {
// some logic here
}
}
在这种情况下,您可以像上面提到的那样使用代码:
MyInterface someObjectThatImplementsMyInterface = new MyClass(42);
因此,一件事是创建一个类定义,另一件事是初始化一个对象,该对象可以扩展某个类或实现某个接口。需要考虑两个不同的事情。 :)
答案 2 :(得分:1)
语法的构造函数部分与类名称不同
从来都不是。构造函数有一个严格的声明,其名称始终是其定义所在类的名称。
您已经看到了一个完全不同的类Dog
的初始化,该类继承自Puppy
。
对您来说,Puppy
是可爱的Dog
。对我来说,Puppy
是a little Rat
。对于其他人,Puppy
可能是a young Seal
。但是它们都是小狗,它们都有一些共同的特征。因此,无论Puppy
的实际类型如何,您都可以使用它。
不过,将Puppy
作为Dog
的子类是一个愚蠢的主意。 MiddleAgedDog
或OldDog
也是如此。您可以简单地问dog.getAge()
,如果它返回20
,我们可以同意这个伙伴很老。
答案 3 :(得分:0)
这是由于面向对象编程中称为“继承”的概念所致。在您的示例中,您不是在创建Puppy
对象,而是在创建Dog
对象。 Puppy
只是继承Dog
的接口或类。
答案 4 :(得分:0)
myPuppy是哪种类型的课程?
变量 myPuppy
的类型为Puppy
,但是(假设此代码已编译)myPuppy
所引用的对象/实例的类型为(或课程)狗。
它在做什么?
这称为inheritance,即如果Dog extends Puppy
,则每条狗都是小狗。因此,Puppy
类型的任何字段或变量都可以引用(或“保留”)任何扩展或实现Puppy
的实例。所知道的是myPuppy
是一只小狗,但不是哪种小狗(假设还有Wolf extends Puppy
-myPuppy
可以指狗或狼)
答案 5 :(得分:0)
这是因为他们在示例中使用继承
你有
public class Puppy{...}
和
public class Dog extends Puppy {
public Dog(String string) {...}
}
您现在可以在Puppy中实现Dog的接口:
public class Dog extends Puppy{...}
然后致电
Puppy myPuppy = new Dog( "tommy" );
您的小狗是狗,并且继承了父级的方法
答案 6 :(得分:0)
动态绑定::在动态绑定中,编译器无法确定要调用的方法。覆盖是动态绑定的完美示例。在覆盖中,父类和子类都具有相同的方法。
这只是意味着何时可以在父类引用中拥有子类对象,如:
Dog myDog = new Puppy( "tommy" );
为此,您的Puppy
类应该是Dog
的子类
JLS 4.10.2. Subtyping among Class and Interface Types
鉴于非泛型类型声明C,类型C的直接超类型如下:
C的直接超类(第8.1.4节)。
C的直接超接口(第8.1.5节)。
还有4.12.2. Variables of Reference Type
类类型T的变量可以保存空引用或对类T或作为T的子类的任何类的实例的引用。
因此,假设您的Dog类中有bark
方法,而您的小狗类中也有覆盖方法,那么
在编译期间,编译器不知道必须调用哪个树皮,因为编译器仅通过引用变量而不是按对象类型进行引用,因此绑定将延迟到运行时,因此将调用树皮的相应版本基于对象的类型。