访问数组中子项 类 的字段

Accessing fields of children classes within an array

我正在测试 Java 中的继承,我有一个带有两个字段的抽象 class 和三个带有自己字段的扩展 class。在另一个 class 中,我在数组中实例化并添加 class 的对象,但我不确定如何访问子 classes 的字段,该数组是主要摘要 class:

这里是摘要 class 和扩展 class 之一的完整代码:

public abstract class MusicRecord {
    private String type;
    private int length;
  
    public MusicRecord(String type, int length){
    this.type = type;
    this.length = length;
    }
  
    public String getType(){
    return type;
    }
    public int getLength(){
    return length;
    }
  
    public void setType(String type){
    this.type = type;
    }
  
    public void setLength(int length){
        this.length = length;
    }
  
    }
public class CD extends MusicRecord {
    private int price;
    private String title;
  
    public CD(String type, int lenght, String title, int price){
        super(type, lenght);
        this.price = price;
        this.title = title;
    }
  
    public String getTitle(){
        return title;
    }
    public int getPrice(){
        return price;
    }
    public String getType(){
        return "MusicRecord: " + super.getType();
    }
    public int getLenght(){
        return super.getLength();
    }
  
    public void setPrice(int price){
        this.price = price;
    }
    public void setTitle(String title){
        this.title = title;
    }
}
import java.util.Arrays;
public class Store {
  
    public static void main(String args[]){
    CD cd1 = new CD("Jaz", 34, "Music 44", 19);
    SD sd1 = new SD("R&B", 45, "Lova is never Lost!!", 21);
    BlueRay br1 = new BlueRay("Hell on Earth", 25, "HipHop", 40);

  
    MusicRecord[] mr = {cd1, sd1, br1,
        new CD("House", 40, "22 Is the Age", 22),
        new SD("Garage", 60, "Boom Boom Boommm", 14),
        new BlueRay("is it time to love", 18, "R&B", 35)};
                  
    for(MusicRecord r : mr){
        System.out.println("Type: " + r.getType() + "\nLength: " + r.getLength());
    }
} 

如何从 subclass/es 中获取字段?

ow I can access the fields of the subclasses, the array is of the main abstract class:

java 类型系统是协变的。 (除了在泛型中,方差是可选的,默认情况下,不变)。对于此答案的其余部分,您需要注意:

  • java.lang.Integer 扩展 java.lang.Number
  • java.lang.Double 扩展 java.lang.Number
  • java.lang.Number 扩展 java.lang.Object

协变意味着您可以采用类型为 Integer 的表达式,并在需要 Number 的地方使用它:当需要超类型时,任何子类型都可以。 (不变性意味着只有特定类型 Number 的表达式才行,而逆变性则相反:如果需要 Integer,Number 或 Object 也可以)。

因此:

Number[] x = new Number[2];
x[0] = new Integer(5);
x[1] = new Double(10.0);

一切都很好。

这也意味着,如果你翻转它,如果一个表达式是 Number 类型,那么它很可能是 Number 的某个子类型。鉴于 Number 是抽象的,这实际上是保证!不能存在 Number 本身的实例,只能存在它的子类的实例。

您可以使用 instanceof 和转换以及模式匹配来处理这个问题:

注意:这使用 java 在 java15!

中引入的新功能
Object[] x = new Object[2];
x[0] = "hello";

if (x[0] instanceof String y) {
    y.toLowerCase();
}

这里我们首先检查 x[0] 是否是一个字符串(它可能是。它也可能是一个整数,或者一个 FileInputStream,或者一个 com.sun.internal.impl.HttpRequestImpl - 协方差意味着事物的实际类型可以,而且通常是,一些无法保证的内部细节,并且可以在 java 版本和实现之间更改)。 如果它是一个字符串,那么对于所有只有在它是(这里是if块的内部)才能到达的代码,y作为一个变量存在,并且包含对完全相同的 object 的引用,但这次输入为字符串,因此您可以在其上自由调用字符串方法,例如 .toLowerCase().

如果您还没有使用 JDK15,'old' 方法是:

if (x[0] instanceof String) {
    String y = (String) x[0];
    y.toLowerCase();
}

这称为强制转换运算符,它只是一个类型断言:如果 x[0] 确实是一个字符串,那么这段代码什么都不做。就好像你写了 (String y = x[0];,除了不会编译,但效果是一样的:y 现在指向相同的东西 x[0] 指向,转换只是告诉编译器你知道这是断言类型,并添加了运行时测试。

另一方面,如果 x[0] 未指向 j.l.String 的某个实例,该行将抛出 ClassCastException。

因此你可以这样做:

int totalPrice = 0;
for (MusicRecord mr : records) {
    if (mr instanceof CD cd) totalPrice += cd.price;
}

注意:在 java 中正确的写法是 Cd,而不是 CD。当然,按照惯例 - java 规范允许您编写任何您想要的内容。但是,如果您希望您的代码与库良好交互并可供其他 java 编码人员阅读,那么遵循既定约定是个好主意,并且这些规定您对所有内容进行标题化,甚至是首字母缩略词,在普通情况下写作,在 all-caps 中呈现。 DVD 播放器,不是 DVD 播放器。光盘,不是光盘。当您使用它时,按照惯例直接访问字段是不行的 - 而是制作吸气剂。应该是 cd.getPrice().

感谢 Lino、rzwitserloot 和 Federico klez Culloca,我能够通过添加以下内容解决问题。

for(MusicRecord r : mr){
      if(r instanceof CD){
        System.out.println("Type: " + r.getType() + "\nLength: " + r.getLength() + "\nTitle: " + ((CD)r).getTitle() + "\nPrice: " + ((CD)r).getPrice() + "\n ***************");
      } else if (r instanceof SD){
        System.out.println("Type: " + r.getType() + "\nLength: " + r.getLength() + "\nTitle: " + ((SD)r).getTitle() + "\nPrice: " + ((SD)r).getPrice() + "\n ***************");
      } else {
        System.out.println("Type: " + r.getType() + "\nLength: " + r.getLength() + "\nTitle: " + ((BlueRay)r).getTitle() + "\nPrice: " + ((BlueRay)r).getPrice() + "\n ***************");
      }