2012-12-14 92 views
5

如果我有JungleCat作为猫的一个子类(JungleCat 延伸猫),然后我说:为什么在实例化对象时需要列出两种对象类型?

JungleCat cat1 = new JungleCat(); 
Cat cat2 = new Cat(); 
Cat cat3 = new JungleCat(); 
JungleCat cat4 = new Cat(); //this one is illegal, right? 
JungleCat cat5; 

我想知道什么是对象类型的cat1cat2cat3cat4,和cat5?我还想知道为什么在实例化一个对象时存在冗余:为什么在实例化一个对象时需要列出两个对象类型。

对不起,如果这是一个非常基本的问题,但它是那种我只想知道一劳永逸,并与良好的推理,我知道我可以期待在这里(而不是雅虎问答等):P

+1

*对象*的类型不一定与它被分配给的变量的类型(或者其他用作表达式)的类型相同。考虑这一点:'Object str =“hello”;' - 分配给'str'的​​ object *的类型是什么? *表达式* str的类型是什么? – 2012-12-14 05:25:05

回答

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 

但是,在上述情况下,你只能够使用的方法和属性在您已实例化的地方。但是,稍后,由于您没有访问该对象的引用,因此您也无法访问它的属性。


现在,让我们继续前进到第2个语句: -

Cat cat = new JungleCat(); 

在这里,你可以猜到,你有Cat - Super Class类型的引用,并JungleCat类型的对象。这就是我们所说的Polymorphism

因此,基本上,您可以创建任何超类型的引用,并使其指向任何子类型的对象。这很容易理解 - “由于JungleCat只是一个Cat,因此,您始终可以有一个Cat参考点到JungleCat”。

相反,这是不正确的。例如: -

JungleCat ref = new Cat(); 

现在这是无效的。因为Cat不一定是JungleCat。它可以是任何其他的猫。因此,您不能将JungleCat参考点指定为Cat对象。


现在,这里是你的实际关注: -

我想知道什么是CAT1,CAT2,CAT3,CAT4和 5类的对象类型

好,cat1cat2 ..不是对象,而是指向某些对象的引用。你可以从上面的解释中推断出它们中的每一个的参考类型。

对象类型是在对象创建语句的RHS上使用的类型。与new关键字一起使用的类型是Object的类型。你可以有不同类型的引用指向相同的对象类型。

因此,您可以将cat1cat2引用指向相同的对象类型。

2

当你实例你必须指定

  1. 的对象究竟是引用类型或类型(类)的新变量能够参考
  2. 的要初始化一个对象,我们需要指定对象的类型(类) ct

所以基本上你需要创建一个JungleCat对象并创建一个指向它的引用。参考指向它能够在

Cat cat3 = new JungleCat();

的情况下,只有JungleCats指向猫(JungleCat,PetCat和所有的猫),如果

JungleCat cat3 = new JungleCat();

关于

object types of cat1, cat2, cat3, cat4, and cat5?

对象类型将是你分配给我t从分配操作员的右侧。

关于

JungleCat cat4 = new Cat(); //this one is illegal, right?

是因为,猫不一定是JungleCat(我们知道这一点的是,它是猫嘛),但JungleCat可以肯定的是一只猫。这就是为什么 Cat cat= new JungleCat();有效

+0

谢谢!这里的所有答案都很有帮助,但你的解释一切都很棒。 – andy

0

JungleCat CAT4 =新猫(); //这个是非法的,对吧?

是的。派生类引用不能指向它的基类。

我在想什么是cat1,cat2,cat3,cat4和cat5的对象类型?

使用getClass方法知道。

为什么在实例化对象时需要列出两种对象类型。

这叫做polymorphism。即一个基类引用可以指向它的任何派生类。

0

-Polymorphism工作作为对对象子类被分配给对象参考变量超一流的

-而且因为这是一个class polymorphism子类必须在同一个继承树,和子类必须是超强型的。

所以

Cat c = new JungleCat(); // Will work 

但是,

JungleCat c = new Cat(); // Won't work 
2

哪些对象类型的cat1cat2cat3cat4cat5

我不知道在这种情况下“对象类型”是什么意思。我建议,而不是思考宣布类型运行时类型

  • cat1的声明类型为JungleCat;运行时间类型cat1也是JungleCat
  • cat2的声明类型是Cat;运行时间类型cat2也是Cat
  • cat3的声明类型是Cat;运行时间类型cat3JungleCat
  • cat4的声明类型是JungleCat; cat4的运行时间类型为Cat
    • 是的,这是非法的。没有明确的downcast它不会编译;用一个明确的演员...它可能但它会在运行时抛出一个异常。
  • 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 

这允许变量的行为根据运行时类型 - 多态 - 而不同,但仍然有编译时检查。

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"); 
    } 
} 

现在,让我们instanstiate一些对象:

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 - 他们不在乎什么样 - 因此,我们不必每次都写两个函数的定义,我们想用一种不同的FileMover,如doSomething(WindowsFileMover m)doSomething(LinuxFileMover m),我们可以简单地repl使用一个功能doSomething(FileMover m),传入正确的类型,它会“正常工作”。

这可能比问题要求更多的信息,但希望它也给你一些直觉,为什么这样的事情以比通常的猫和狗稍微更具体的方式完成。

+0

“这是多态性背后的基本原理。”你可以拥有多态性而无需静态类型或变量声明。 –

+1

@RichardJPLeGuen没错,你可以。也许我应该在那里放上括号,“这是多态(Java中)的基本原理”。 – Yuushi

相关问题