Java arraylist and polymorphism

8.4k Views Asked by At

Recently I find some questions about 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());

Do these two pieces have compilation errors? I guess there should be something wrong with add(new String()) but can't make sense clearly.

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

what if I use this (below)?

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

I was struggled with these questions for two days so I really need somebody to give me some explanation. thanks in advance

2

There are 2 best solutions below

0
Freaky Thommi On

By default you can put any Object into a List, but from Java 5, Java Generics makes it possible to limit the types of object you can insert into a List. Here is an example:

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

This List can now only have MyObject instances inserted into it. You can then access and iterate its elements without casting them. Here is how it looks:

   MyObject myObject = list.get(0);

The below content is from oracle's website

In a nutshell, generics enable types (classes and interfaces) to be parameters when defining classes, interfaces and methods. Much like the more familiar formal parameters used in method declarations, type parameters provide a way for you to re-use the same code with different inputs. The difference is that the inputs to formal parameters are values, while the inputs to type parameters are types.

Code that uses generics has many benefits over non-generic code:

Stronger type checks at compile time. A Java compiler applies strong type checking to generic code and issues errors if the code violates type safety. Fixing compile-time errors is easier than fixing runtime errors, which can be difficult to find.

Elimination of casts. The following code snippet without generics requires casting:

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

When re-written to use generics, the code does not require casting:

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

// no cast Enabling programmers to implement generic algorithms. By using generics, programmers can implement generic algorithms that work on collections of different types, can be customized, and are type safe and easier to read.

1
chiastic-security On

For questions 1 and 2, the key thing you need to learn about is generics.

If you write

ArrayList dates = new ArrayList();

then you get a raw type: the compiler doesn't know what sort of thing you can put into your ArrayList. But when you write

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

then the compiler knows that this ArrayList will store instances of Date, and it can do some checking that you're only trying to insert and retrieve Date values. This is there both to protect and to avoid unnecessary gymnastics. With this second one, you will find that

dates.add(new String());

won't compile because the compiler knows that you're trying to put something of the wrong type into the list. Similarly, you can just write

Date date = dates.get(0);

because the compiler knows that what's inside will be a Date. With your first form, that wouldn't be the case: the compiler can't enforce any type checking, and so you would need to cast it when you got it out:

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

Using raw types can lead to errors in your program where you put the wrong type in by accident and the compiler won't be able to stop you; and it also makes it unnecessarily verbose when you retrieve things because you have to do the casting yourself. Think of the generic type parameter (the <Date> part) as being a way of enforcing what can go into, and come out of, the list. (Technically, this is only enforced by the compiler, and not by the runtime, but that's a lesson for another day... look up type erasure if you're interested.)

For this code:

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

you hit one of the most annoying aspects of the type system. Although Student is a subtype of Person, that doesn't mean that ArrayList<Student> is a subtype of ArrayList<Person>. It would be nice if it were so, but it's not. So:

Person p = new Student();

This line above is fine. A Student is a Person, so a Person reference can hold a Student instance.

Student s = new Person();

You can't do this line above, though. The reference s has to hold a reference to a Student; and a Person is not necessarily a Student. This will give you a compile-time error.

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

It would be nice if this one worked, but it doesn't. If you want an ArrayList<Student> then you have to have List<Student> as the formal type of lp.

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

This wouldn't work under any circumstances, for the same reason that the second line failed: a Person isn't necessarily a Student, and the List<Student> can't be expected to hold anything that isn't a Student.