我是新来的,所以如果我不遵守标准礼仪,我会提前道歉。
我想知道如何使用ArrayLists从子类中获取数据。
import java.io.*;
import java.util.*;
public class MyProgrammingLab {
public static void main(String[] args){
ArrayList<Test> testArray = new ArrayList<>();
testArray.add(new Test("First"));
testArray.add(new SubTest("Last"));
System.out.println(testArray.get(0).getFirstName());
System.out.println(testArray.get(1).getLastName());
}
}
public class Test {
private String firstName;
public Test(){
}
public Test(String firstName){
this.firstName = firstName;
}
public String getFirstName() {
return firstName;
}
}
public class SubTest extends Test{
private String lastName;
public SubTest(){
super();
}
public SubTest(String lastName){
this.lastName = lastName;
}
public String getLastName(){
return lastName;
}
}
我的主要系列“System.out.println(test.Array.get(1).getLastName());”向我抛出错误,我无法弄清楚为什么?
在我的主程序中,我将使用大小超过1的ArrayList。
提前谢谢!
答案 0 :(得分:4)
我建议非常谨慎关于使用我即将提出的解决方案,但是:
System.out.println(((SubTest) testArray.get(1)).getLastName());
但是,只有在确定位置1的元素是SubTest时才会执行此操作,否则您将获得ClassCastException
。
如果你不确定,你应该检查一下。
Test whatIGot = testArray.get(1);
if(whatIGot instanceof SubTest) {
SubTest whatIActuallyGot = (SubTest)whatIGot;
System.out.println(whatIActuallyGot.getLastName());
}
通常,当您拥有超类类型的变量时,应该避免依赖于子类的属性。这样做会产生代码异味,可能是设计不良造成的。
正确的解决方案包括问自己这两个问题:
getLastName
属于SubTest
而不属于Test
吗?如果getLastName()
位于Test
,则可以完全解决问题。getLastName
元素访问testArray
吗?如果getLastName()
属于Test
(Test
的所有实例都有某些姓氏),但您没有它的默认值很好,然后考虑使用抽象方法Test
使getLastName
成为抽象类。请注意,这会使new Test()
停止工作。
答案 1 :(得分:1)
您的列表声明为Arraylist<Test>
。这意味着它很灵活:您可以将Test
的任何实例(包括子类的实例)放入其中。
这种灵活性带来了一个限制:稍后,当你再次获得一个元素时,所有你能说出的元素就是它是Test
的一个实例 - 正如你所发现的,你无法分辨没有特定类型检查条目是Test
还是SubTest
。 (如果列表必须返回条目的确切类型,请考虑如何在get
上编写ArrayList
方法 - 方法的返回类型将根据传入的索引而改变!)< / p>
如果您需要这种灵活性(并且您的示例代码建议您这样做,因为您还在Test
旁边存储SubTest
的实例),您最简单的选择是检查类型和强制转换:
Test secondEntry = testArray.get(1);
if (secondEntry instanceof SubTest) {
System.out.println(((SubTest) secondEntry).getLastName());
}
虽然正如其他人所指出的那样,这在某种程度上违背了使用泛型的观点。
其他替代方案包括:
getLastName
方法移至父类,然后在子类中重写它(如果父类有意义使用此方法); 这看起来像是:
import java.io.*;
import java.util.*;
public class MyProgrammingLab {
public static void main(String[] args) {
ArrayList<Test> testArray = new ArrayList<Test>();
testArray.add(new Test("First"));
testArray.add(new SubTest("Last"));
for (Test aTest : testArray) {
emit(aTest);
}
}
public static void emit(Test aTest) {
if (aTest instanceof SubTest) {
System.out.println(((SubTest) aTest).getLastName());
} else {
System.out.println(aTest.getFirstName());
}
}
}
答案 2 :(得分:0)
testArray.get(1)
返回的是Test
类型,而不是SubTest
。如果您完全确定它是SubTest
(您可以instanceof
进行检查),那么您可以使用
System.out.println(((SubTest) testArray.get(1)).getLastName());
答案 3 :(得分:0)
因为你这样宣布:
ArrayList<Test> testArray;
Java只知道ArrayList
包含Test
个对象。因此,不知道关于子类中的任何方法,即使对象具有该方法。
如果你添加:
public String getLastName(){
return ""; // do nothing
}
对于Test
课程,它会按照您的预期进行编译和工作,因为现在 Test
个对象具有getLastName()
方法。
其他解决方案建议投射,但我强烈建议不要投射,因为这违背了使用泛型的目的。您不希望处于强制转换然后处理ClassCastException
的情况。泛型的要点是优雅地处理与你的情况完全相同的情况。
答案 4 :(得分:0)
当您声明一个ArrayList时,您明确表示您希望列出完全 Test
的内容。您可以使用Generics来包含Test以及扩展它的任何内容,这是ArrayList<? extends Test>
所做的。
虽然这可以解决您的确切问题,但您应该知道extend concrete classes的设计很差,您应该使用中间实例变量(例如Test test1 = testArray.get(0)
)来避免违反Law of Demeter。