Java 什么时候应该或不应该';我是否使用更通用的变量类型来引用我的对象?

Java 什么时候应该或不应该';我是否使用更通用的变量类型来引用我的对象?,java,subtype,Java,Subtype,有两类A和B,B扩展A。两者的区别是什么 A a = new B(); 及 两者都创建了类B的对象。区别是什么?优点是 Fruit f = new Mango(); 假设 consumeFruit(Fruit f); 现在你可以打电话了 consumeFruit(new Mango()); consumeFruit(new Strawberry()); 注意: 对于这种情况,您将只能调用引用类型中声明的方法。对象类型的版本将被调用。您将只访问引用类型的类中的字段 另请参见 只有A的

有两类
A
B
B
扩展
A
。两者的区别是什么

A a = new B();

两者都创建了类B的对象。区别是什么?

优点是

Fruit f = new Mango();
假设

consumeFruit(Fruit f);
现在你可以打电话了

consumeFruit(new Mango());
consumeFruit(new Strawberry());
注意: 对于这种情况,您将只能调用引用类型中声明的方法。对象类型的版本将被调用。您将只访问引用类型的类中的字段

另请参见

只有A的属性和方法

B b = new B();
具有B的属性和方法。
如果向B添加了一些属性或方法,则不能用a调用它们。

这个问题是关于多态性的。以下是Kathy Siera的摘录:


公共类测试动画{
公共静态void main(字符串[]参数){
动物a=新动物();
动物b=新马();//动物参考,但马对象
a、 eat();//运行eat()的动物版本
b、 eat();//运行马版eat()
}
}
动物类{
公共空间({
System.out.println(“一般性动物饮食”);
}
}
马类动物{
私人空间吃(){//哇!-这是私人空间!
System.out.println(“吃马的干草、燕麦,”
+“和马招待”);
}
}

如果编译了此代码(它没有编译),则以下操作将在运行时失败:
动物b=新马();//动物参考号,但是一匹马
//对象,到目前为止还不错
b、 吃();//运行时崩溃

假设以下示例:

我们有甲级动物:

public class Animal {

  public void eat() {
     // each animal can eat
  }
}
现在我们有另一类狗:

public class Dog  extends Animal {

   public void bark() {
      // dogs can bark
   }
}
现在我们可以编写以下代码:

Animal pet = new Dog();
现在我们知道,
pet
可以吃东西,但除此之外什么都不能吃。但是如果我们写

Dog pet = new Dog(); 
然后我们知道,我们的宠物可以吃东西和吠叫

还有安全和不安全铸造。安全施法从
动物
,因为每只狗都是动物(扩展它)

但是,如果我们想把动物扔给狗,我们必须测试动物的实例是否真的是狗,因为它不一定是狗

Animal pet = new Dog();
Dog myDog = null;
if (pet instanceof Dog) {
    myDog = (Dog) pet;
}
如果你说

List a = new ArrayList();
然后您只在代码中的一个位置引用ArrayList。这使得以后更容易将其更改为其他内容,如LinkedList

当然,如果您需要特定于ArrayList的方法,这是行不通的


一般来说,您应该使用最通用的类型。

通常,当父类变量可能被分配到不同的对象时,声明父类并为其分配继承的类非常有用。比如说

Pet p;
if (favoritePet == Pets.CAT) {
   p = new Cat();
} else {
   p = new Dog();
}

System.out.println(p.someMethodFromPet());

在这两种情况下,都会创建类
B
的对象,这是正确的。这两个声明之间的区别在于变量的类型

记住变量和对象之间的区别是非常重要的。例如,以下代码定义了3变量,但仅定义了2对象:

Circle c1 = new Circle(5);
Circle c2 = c1;
Circle c3 = new Circle(5);
当你说

Shape s = new Circle(5);
而不是

Circle s = new Circle(5);
假设
Circle
扩展
Shape
,那么,即使在这两种情况下都创建了一个Circle对象,在前一种情况下,您只能调用圆上的Shape方法(通过变量
s
),而在第二种情况下,您可以调用所有的Circle方法(因为您将通过圆变量
c
)调用它们。类似于
s.getArea()
的调用在这两种情况下都有效,但类似于
s.getRadius()
的调用只允许在第二种情况下使用(除非使用难看的强制转换)

那么,为什么我们经常像第一种情况那样做呢?也就是说,为什么我们经常定义一种比必要的更通用的变量?通常我们这样做是因为我们想限制接口的安全性。也许我们只关心形状,但在这种情况下,特定的形状恰好是一个圆。如果你关心圆的指定如果是ic属性,那么我们应该使用圆形变量。但我们应该尽可能做到通用。编码到最通用的接口允许我们的代码在不修改的情况下处理圆形以外的形状


当然,要真正理解这一点,你必须亲身体验,但希望这一解释只是一个开始。我相信有很多书籍、博客帖子和文章都会用有用的真实轶事来更详细地解释这一点。

注意,你仍然可以用
Mango f=new Mango()调用这些方法
-1:您没有回答关于两段代码之间差异的问题,只谈到了一些优势。@罗兰·伊利格的优势就是其中的差异。另请参见备注部分……除非您将
a
转换为
B
那么,对象拥有它所有的属性,不管您如何称呼它。它将被隐藏来自编译器。@Thilo:谢谢,这对我来说是新的。我只知道,你不能使用它们。A=新B();创建A的对象,但引用类A。对象A获取类A的属性。相关,但不是直接回答:-1这是关于接口的编程,而不是关于继承。坏例子我认为它同样适用于父类(也通过它们公开的方法隐式定义接口)实际上,这个例子正好解释了问题背后的原理:信息隐藏。
Circle c1 = new Circle(5);
Circle c2 = c1;
Circle c3 = new Circle(5);
Shape s = new Circle(5);
Circle s = new Circle(5);