【问题标题】:Object type declaration对象类型声明
【发布时间】:2013-10-12 18:09:10
【问题描述】:

好的.. 所以, 当您具有类的层次结构时,例如

public class A {...}

和,

public class B extends A {...}

...创建对象时,有什么区别:

A object = new A();
A object = new B();
B object = new B();

感谢您的宝贵时间。

【问题讨论】:

  • 当你创建 (new) 一个 A 时,你创建了一个 A。当你创建 B 时,你创建了一个 B。但是 B 包含了 A 的所有功能,并且可以像 A 一样使用大多数情况。如果你把对 B 的引用放在一个类型为“A”的变量中,你就不能访问对象的“B-ness”,只能访问“A-ness”。但是,您可以将引用“转换”回“B”以重新访问它的“B-ness”:B refB = (B)refA; 但是如果 refA 实际上没有引用 B,则转换((B))将在运行时失败。

标签: java class inheritance


【解决方案1】:

一行

A var = new B();

是两个独立步骤的简写。

A var;         // (1) Make a variable of TYPE A.
var = new B(); // (2) Make an object of CLASS B, that from now on may be 
               // referred to by the variable var.

所以一个变量有一个TYPE,一个对象有一个CLASS。他们经常匹配。变量的类型通常实际上是一个类,尽管不一定。理解变量的类型和变量所引用的对象的类之间的区别很重要。

一个对象通常属于多个类。如果 B 类扩展了 A 类,这意味着 B 类的所有对象也是 A 类的对象。并且任何类的所有对象也是Object 类的对象。换句话说,当我们说一个对象是 B 时,这比说它是 A 更具体。就像我们说 Yogi 是 bear 时,这比说 Yogi 是 动物,因为所有的都是动物

因此,如果 A 是 B 扩展的类,则 A 类型的变量确实可以引用 B 类的对象。但是如果你有一个 A 类型的变量,你就不能用它来做特定于 B 类型的对象的事情。例如,假设 A 类有一个名为 display() 的方法,而 B 类有一个名为explain()。编译器将允许您在类型 A 的变量上调用 display(),但不允许您调用 explain()。如果是这样,则尝试在实际上不是 B 的对象上调用 explain() 会有风险,这会失败。

因此,只要有 B 类定义的方法,您就需要一个 B 类型的变量才能调用它们。当然,你也可以使用同一个变量来调用类 A 中定义的方法。那么从某种意义上说,如果类 B 扩展类 A,那么 B 类型的变量比 A 类型的变量更强大——你可以用它做更多的事情。

所以问题来了——我为什么要写作

A var = new B();

在这个例子中,什么时候类型 B 的变量会比 var 更强大?

简短的回答是它与查看代码的人交流。它说,“是的,我知道这个变量指的是 B,但我实际上只打算使用 A 类提供的方法。这实际上对试图理解你的代码或维护它的人很有帮助。

在某些情况下,它可以对涉及该变量的方法调用产生真正的影响。假设有另一个类 C,它有两个名称相同但签名略有不同的方法,像这样。

public class C {
    public void process(A arg){
        // Do some stuff
    }

    public void process(B arg){
        // Do some other stuff
    }
}

在这种特殊情况下,被调用的process 的版本取决于变量的类型,而不是对象的类。所以如果你写

C processor = new C();
A var = new B();
processor.process(var);

这将调用process 的第一个版本 - 签名中带有 A 的版本。因为变量的类型。但是如果你写

C processor = new C();
B var = new B();
processor.process(var);

这将调用process 的第二个版本 - 签名中带有 B 的那个。

【讨论】:

    【解决方案2】:
    A object = new A();
    

    您正在 A 类型的引用中创建 A instance。您只能访问 A 方法/属性和父方法/属性。

    A object = new B();
    

    您正在 A 类型的引用中创建 B instance。这样,object 可以以多态方式运行,例如,如果您在 B 中覆盖 object.method()method,那么它将调用它覆盖方法。你必须小心不要破坏Liskov Substitution Principle。您只能访问 A 方法/属性和父方法/属性。当您只需要超类型合同时,这是首选方式。

    B object = new B();
    

    您正在B 类型的引用变量中创建B instance。您只能访问 B 方法/属性和父方法/属性。

    【讨论】:

      【解决方案3】:
      A object = new B();
      

      这声明object 将引用类A 或其任何子类的对象(当它不是null 时)。编译器会将其视为A 类型的对象,因此您只能访问为A(或其超类之一)声明的方法和字段。这也意味着您可以稍后将其分配给属于 A 类或其子类的任何 other 对象:

      A object1 = new B();
      B object2 = new B();
      
      // reassign later
      object1 = new A();  // legal
      object2 = new A();  // ILLEGAL
      
      class C extends A { ... }
      object1 = new C();  // legal
      object2 = new C();  // ILLEGAL
      

      所以初始声明将object 声明为具有A 类型。但是它的初始值是B类型的对象,这是可以的,因为BA的子类。

      这应该可以解释您的第二个和第三个示例之间的区别。第一个和第二个之间的区别只是(在运行时)第一个创建A 类型的新对象,第二个创建B 类型的新对象。

      【讨论】:

        【解决方案4】:
        A object1 = new A();
        A object2 = new B();
        B object3 = new B();
        

        object1 被声明为对 A 对象的引用。由于 B 类扩展了 A 类,因此可以将其设置为 or(new A()new B() 有效)。

        object2 被声明为对 A 对象的引用,但实际上是 B 对象。假设 B 类有一个名为eatFood() 的方法。如果您尝试使用object2.eatFood() 访问该方法,编译器会抛出错误,因为eatFood 方法仅在B 类中。即使该对象实际上是一个 B 对象,由于类型声明,编译器仍认为它是一个 A 对象。要访问eatFood 方法,您必须对其进行类型转换:((B)object2).eatFood()

        object3 只是对 B 对象的引用,实际上是 B 对象。它可以访问 A 方法和 B 方法。

        【讨论】:

        • 我认为应该是((B)object2).eatFood(),因为演员运算符的优先级......我自己一直遇到这个问题。
        【解决方案5】:
        A object = new A();
        

        object 类型为 A(您可以从 A 访问字段或方法)

        A object = new B();
        

        object 类型为 A(您不能从 B 访问字段或方法,只能从 A 访问)

        B object = new B();
        

        object 类型为 B(您可以访问来自 AB 的字段或方法)

        【讨论】:

          【解决方案6】:
          public class A
          {
              public void methodA(){}
          }
          public class B extends A
          {
              public void methodB(){}
          }
          

          我希望这可以证明差异。

          A obj = new A();
          
          a.methodA(); //works
          
          A obj = new B();
          
          obj.methodA(); //works
          obj.methodB(); //doesn't work
          ((B)obj).methodB(); //works
          
          B obj = new B();
          
          obj.methodA(); //works
          obj.methodB(); //works
          

          【讨论】:

            猜你喜欢
            • 2023-03-20
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2021-01-20
            • 1970-01-01
            相关资源
            最近更新 更多