访问数组中子类的字段

问题描述

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

这里是抽象类和扩展类之一的完整代码

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

如何从子类/es 中获取字段

解决方法

现在我可以访问子类的字段,数组是主抽象类的:

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 和强制转换以及模式匹配来处理此问题:

注意:这使用了 java15 中引入的新 java 特性!

Object[] x = new Object[2];
x[0] = "hello";

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

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

如果您还没有使用 JDK15,那么“旧”方法是:

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 编码人员可读,那么遵循既定的约定是一个好主意,并且这些约定表明您将所有内容,甚至是在普通写作中呈现的首字母缩略词全部大写。 DvdPlayer,而不是 DVDPlayer。光盘,不是光盘。当您使用它时,按照惯例直接访问字段是不行的 - 而是使用 getter。应该是 cd.getPrice()

,

感谢 Lino、rzwitserloot 和 Federico klez Culloca,我通过以下添加解决了这个问题。

<Picker dropdownIconColor= 'colorName'/>