如果我将JungleCat作为Cat的子类(JungleCat extends Cat),那么我说:
JungleCat cat1 = new JungleCat();
Cat cat2 = new Cat();
Cat cat3 = new JungleCat();
JungleCat cat4 = new Cat(); //this one is illegal, right?
JungleCat cat5;
我想知道cat1
,cat2
,cat3
,cat4
和cat5
的对象类型是什么?我也想知道为什么在实例化对象时存在冗余:为什么在实例化对象时需要列出两个对象类型。
我很抱歉,如果这是一个非常基本的问题,但是我只是想知道一劳永逸并且有一个很好的理由得到一个很好的答案,我知道我可以期待这里(而不是雅虎答案等):P
答案 0 :(得分:5)
在以下声明中: -
JungleCat cat1 = new JungleCat();
您可以将其分为两部分: -
JungleCat cat1; // This creates a reference of type `JungleCat`
cat1 = new JungleCat(); // This creates an object of type `JungleCat`.
现在,您正在进行cat1
引用,指向JungleCat
对象。引用只不过是指向所创建对象的链接,因此您可以访问它们。
您也可以像这样创建对象: -
new JungleCat(); // Will create an unnamed object
但是,在上面的例子中,您只能在实例化的地方使用方法和属性。但是,稍后,由于您没有访问该对象的参考,您也无法访问它的属性。
现在,让我们继续讨论第二个声明: -
Cat cat = new JungleCat();
在这里,您可以猜测,您引用了类型Cat - Super Class
和JungleCat
类型的对象。这就是我们所说的Polymorphism
。
因此,基本上,您可以创建任何超类型的引用,并使其指向任何子类型的对象。这很容易理解 - “因为JungleCat只是一只猫。所以,你总能有一个Cat参考点指向JungleCat”。
反过来说这不是真的。例如: -
JungleCat ref = new Cat();
现在这无效。因为Cat
不一定是JungleCat
。它可以是任何其他猫。因此,您无法将JungleCat
引用点指向Cat
对象。
现在这是实际关注: -
我想知道cat1,cat2,cat3,cat4和。的对象类型是什么 CAT5
嗯,cat1
,cat2
..不是对象,而是指向某些对象的引用。您可以从上面的解释中推断出每个参考类型。
对象类型是对象创建语句的RHS上使用的类型。与new
关键字一起使用的类型是Object
的类型。您可以使用指向同一对象类型的不同类型的引用。
因此,您可以同时指向同一对象类型的cat1
和cat2
个引用。
答案 1 :(得分:2)
实例化对象时,必须指定
所以基本上你需要创建一个JungleCat对象,并创建一个指向它的引用。
指向它的引用能够指向Cat(JungleCat,PetCat和所有猫) Cat cat3 = new JungleCat();
只有JungleCats
JungleCat cat3 = new JungleCat();
关于
object types of cat1, cat2, cat3, cat4, and cat5?
对象类型将从赋值操作符的右侧分配给它。
关于
JungleCat cat4 = new Cat(); //this one is illegal, right?
是的因为,一只猫不一定是JungleCat(我们只知道它是一只猫,它是一只猫),但JungleCat肯定是一只猫。这就是为什么
Cat cat= new JungleCat();
有效
答案 2 :(得分:2)
cat1
,cat2
,cat3
,cat4
和cat5
的对象类型是什么?
我不知道“对象类型”在这种情况下意味着什么。我建议用声明类型和运行时类型来考虑事情。
cat1
类型为JungleCat
;运行时类型cat1
也是JungleCat
cat2
类型为Cat
;运行时类型cat2
也是Cat
cat3
类型为Cat
;运行时类型cat3
为JungleCat
cat4
类型为JungleCat
;运行时类型cat4
为Cat
cat5
类型为JungleCat
;它没有运行时类型。“冗余”的要点是因为Java是静态类型的,并且还支持动态调度。静态类型要求变量具有声明的类型;动态调度允许运行时类型与声明的类型不同。
例如,您可以在程序执行过程中为同一个变量分配多个对象:
Cat c1 = new Cat();
c1 = new JungleCat();
声明的c1
类型确定可以通过该变量调用哪些方法:
Cat c1 = new Cat();
c1.purr(); // compiles without error
c1 = new JungleCat();
c1.purr(); // compiles without error
同样,它确定不能通过该变量调用哪些方法:
JungleCat jc1 = new JungleCat();
Cat c1 = jc1;
jc1.roar(); // compiles without error - JungleCats can roar!
c1.roar(); // compile error! Cats don't roar
这允许变量的行为因运行时类型 - 多态性而异 - 同时仍然需要编译时检查。
答案 3 :(得分:0)
JungleCat cat4 = new Cat(); //这个是非法的,对吧?
是。派生类引用不能指向它的基类。
我想知道cat1,cat2,cat3,cat4和cat5的对象类型是什么?
使用getClass
方法知道。
为什么在实例化对象时需要列出两个对象类型。
这称为polymorphism
。即一个基类引用可以指向它的任何派生类。
答案 4 :(得分:0)
- Polymorphism
正在为子类的对象分配对象引用变量强> 超级。
- 由于这是class polymorphism
,子类必须位于同一继承树中,并且子类必须是超类型的子类。 强>
<强>因此强>
Cat c = new JungleCat(); // Will work
<强>但是,强>
JungleCat c = new Cat(); // Won't work
答案 5 :(得分:0)
这是多态性背后的基本原理。每个对象的类型出现在表达式的左侧,但是,它将像右侧的任何内容一样。例如,让我们想一个像这样的函数:
public static void Claw(Cat cat)
{
cat.claw();
}
现在,我们假设我们的类类似于:
public class Cat
{
//...
public void claw()
{
System.out.println("Cat claw");
}
}
public class JungleCat extends Cat
{
//...
@Override
public void claw()
{
System.out.println("Jungle cat claw");
}
}
现在,让我们实现一些对象:
Cat c = new Cat();
Cat j = new JungleCat();
传入我们的static Claw
函数:
Claw(c); //Prints "Cat claw"
Claw(j); //Prints "Jungle cat claw"
假设我们修改了这样的函数:
public static void Claw(JungleCat junglecat)
{
junglecat.claw();
}
然后Claw(c)
显然不会编译,因为它是Cat
。那么Claw(j)
呢?这也不会编译,因为我们已经说它也是类型Cat
。我们知道它实际上是JungleCat
,但我们已经告诉编译器“好的,只需将它视为普通的Cat
类型”。
许多这些初学者例子中经常缺少的是为什么有人想要这样做的动机。猫和动物等通常是不好的例子。更好的例子是,假设我们有一些代码可以根据运行的操作系统执行不同的操作;说移动文件。如果它在Linux
上运行,我们希望将文件移至/usr/bin
,如果它在Windows上C:\Windows\System32
。所以我们定义一个这样的界面:
public interface FileMover
{
public void move(File f);
}
public class LinuxMover implements FileMover
{
public void move(File f)
{
//Move our file to /usr/bin
}
}
public class WindowsMover implements FileMover
{
public void move(File f)
{
//Move our file to C:\Windows\System32
}
}
现在,我们不知道这个代码将在运行时运行哪个系统 - 所以我们可以在运行时检查并实例化正确的类:
//Pseudocode
FileMover fm;
if(OperatingSystem == LINUX) {
fm = new LinuxMover();
}
else if(OperatingSystem == WINDOWS) {
fm = new WindowsMover();
}
fm.move();
同样,任何函数都可以简单地使用FileMover
- 它们不关心 kind - 因此我们每次想要使用时都不必编写两个函数定义不同类型的FileMover
,例如doSomething(WindowsFileMover m)
和doSomething(LinuxFileMover m)
,我们可以简单地用一个函数doSomething(FileMover m)
替换它,传递正确的类型,它将“正常工作”。
这可能比问题要求的信息要多得多,但希望它也能让你直截了当地说明为什么这些事情比通常的猫狗更具体一些。