从子类中获取数据:Java

时间:2012-11-26 23:29:44

标签: java inheritance polymorphism

我是新来的,所以如果我不遵守标准礼仪,我会提前道歉。

我想知道如何使用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。

提前谢谢!

5 个答案:

答案 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() 属于TestTest的所有实例都有某些姓氏),但您没有它的默认值很好,然后考虑使用抽象方法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方法移至父类,然后在子类中重写它(如果父类有意义使用此方法);
  • 使用基于类型的调度实现strategy pattern

这看起来像是:

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)

查看Java Inheritence tutorial

可能有所帮助

当您声明一个ArrayList时,您明确表示您希望列出完全 Test的内容。您可以使用Generics来包含Test以及扩展它的任何内容,这是ArrayList<? extends Test>所做的。

虽然这可以解决您的确切问题,但您应该知道extend concrete classes的设计很差,您应该使用中间实例变量(例如Test test1 = testArray.get(0))来避免违反Law of Demeter