问题描述
我正在 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'/>