Java数组列表和多态性

3
最近我发现了一些关于Java的问题。 1【A】
ArrayList dates = new ArrayList();
dates.add(new Date());
dates.add(new String());

【B】

ArrayList<Date> dates = new ArrayList<Date>();
dates.add(new Date());
dates.add(new String());

这两个代码片段是否有编译错误?我猜测add(new String())这里可能有问题,但不太清楚。
  1. I cannot find the mistake in this arraylist, is the return type of dates.get() wrong?

        ArrayList dates = new ArrayList();
        dates.add(new Date());
        Date date = dates.get(0);
    
如果我使用以下内容会怎样?
ArrayList<Date> dates = new ArrayList<Date>();
dates.add(new Date());
Date date = dates.get(0);
  1. If Student is the subtype of the Person, then which are legal?

    Person p = new Student();
    Student s = new Person();
    List<Person> lp = new ArrayList<Student>();
    List<Student> ls = new ArrayList<Person>();
    

我为这些问题苦苦思索了两天,真的需要有人给我一些解释。谢谢提前。

2个回答

3
对于问题1和2,您需要学习的关键是“泛型”。
如果您编写了以下内容:
ArrayList dates = new ArrayList();

那么你就会得到一个 原始类型:编译器不知道你可以放入 ArrayList 中的是什么样的东西。但当你写下以下代码时:

ArrayList<Date> dates = new ArrayList<Date>();

如果编译器知道这个ArrayList将存储Date实例,它会检查你是否只尝试插入和检索Date值。 这既是为了保护,也是为了避免不必要的麻烦。通过第二种方式,您会发现

dates.add(new String());

如果编译器发现你试图将错误类型的内容放入列表中,它将不会编译。同样地,你可以直接写:

Date date = dates.get(0);

这是因为编译器知道里面的内容会是一个Date。但是使用第一种形式就不一样了:编译器无法强制执行任何类型检查,所以当你取出数据时需要进行强制转换:

Date date = (Date) dates.get(0);

使用原始类型可能会导致程序中发生错误,因为您可能会意外地将错误的类型放入其中,而编译器无法阻止您;此外,当您检索内容时,它也会使得代码冗长不必要,因为您必须自己进行类型转换。将泛型类型参数(即<Date>部分)视为强制要求列表中的内容能够输入和输出的方式。 (从技术上讲,这仅由编译器强制执行,而不是运行时,但那是另一天的教训......如果您感兴趣,请查找类型擦除。)
对于这段代码:
Person p = new Student();
Student s = new Person();
List<Person> lp = new ArrayList<Student>();
List<Student> ls = new ArrayList<Person>();

你遇到了类型系统中最让人恼火的问题之一。虽然 StudentPerson 的一个子类型,但这并不意味着 ArrayList<Student>ArrayList<Person> 的一个子类型。如果它是这样就太好了,但事实并非如此。因此:

Person p = new Student();

上面这行没问题。一个Student是一个Person,所以一个Person的引用可以持有一个Student实例。

Student s = new Person();

然而您不能使用上述代码行。引用变量s必须指向一个Student对象;但是Person不一定是Student,这会导致编译时错误。

List<Person> lp = new ArrayList<Student>();

如果这个方法能够正常工作就好了,但是它并没有如此。如果你想要一个 ArrayList<Student> ,那么你必须把 lp 的形式类型设为 List<Student>

List<Student> ls = new ArrayList<Person>();

无论在什么情况下都不会起作用,原因和第二行失败的原因相同:一个Person不一定是StudentList<Student>不能期望容纳任何不是Student的东西。


尽管不可能执行 List<Person> lp = new ArrayList<Student>(),但仍有一些情况可以使用多态性:https://dev59.com/9FTTa4cB1Zd3GeqPqDTn#4959000 - Baumann

0

默认情况下,您可以将任何对象放入列表中,但从Java 5开始,Java泛型使得限制您可以插入到列表中的对象类型成为可能。以下是一个示例:

List<MyObject> list = new ArrayList<MyObject>();

这个列表现在只能插入MyObject实例。然后,您可以访问和迭代其元素而无需进行转换。以下是它的样子:

   MyObject myObject = list.get(0);

以下内容来自Oracle网站

简而言之,泛型使得类型(类和接口)在定义类、接口和方法时成为参数。就像方法声明中使用的更熟悉的形式参数一样,类型参数提供了一种用不同输入重用相同代码的方法。不同之处在于,实际参数的输入是值,而类型参数的输入是类型。

使用泛型的代码具有许多优点,比非泛型代码有更强的类型检查:

编译时更强的类型检查。Java编译器对泛型代码应用强类型检查,并在代码违反类型安全性时发出错误。修复编译时错误比修复运行时错误更容易找到。

消除强制类型转换。没有泛型的以下代码片段需要转换:

List list = new ArrayList();
list.add("hello");
String s = (String) list.get(0);

当使用泛型重写代码时,代码不需要转换类型:
List<String> list = new ArrayList<String>();
list.add("hello");
String s = list.get(0); 

// 没有强制类型转换 通过使用泛型,程序员可以实现适用于不同类型集合的通用算法,这些算法可以进行定制,具有类型安全性和更易于阅读的特点。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接