7
public void run(){
    setFont("Courier-24");
    //Define list as ArrayList<Integer>
    ArrayList<Integer> list = new ArrayList<Integer>();
    readList(list);
}

private void readList(ArrayList list){
    list.add("Hello");
    list.add(2);
    println("list = "+list);
    println("Type of list = "+list.get(0).getClass());
    println("Type of list = "+list.get(1).getClass());
}

Result:

list = [Hello, 2]
Type of list = class java.lang.String
Type of list = class java.lang.Integer

Here is my code and result. My question is, how is it possible that ArrayList of type Integer can store String objects? What's the type of list now? And what mechanism is this?

kahofanfan
  • 297
  • 1
  • 10
  • 2
    1. You are using `Raw` type in `readList()`. 2. Read about `type erasure`. – PM 77-1 May 27 '15 at 21:27
  • You are **not** retrieving a type of `List`. You are retrieving the type of the **first element** of the `List`. – PM 77-1 May 27 '15 at 21:29
  • Even though *"Java"* is better than *"JAVA"* it still doesn't belong to the title. – Tom May 27 '15 at 21:30
  • Work through [this tutorial](https://docs.oracle.com/javase/tutorial/java/generics/) all the way from top to bottom. – PM 77-1 May 27 '15 at 21:30
  • @PM77-1 I know I'm retrieving the type of the first two elements of list, and the type of list is now just ArrayList. So that's type erasure? – kahofanfan May 27 '15 at 21:33
  • I gave you the tutorial link. Spend enough time to absorb this information. Then (if you still have questions), come back and ask specific questions. – PM 77-1 May 27 '15 at 21:36
  • @Tom I'm sorry...My first time posting a question, I didn't realize other people can edit my title so I think system went wrong and I changed it again... – kahofanfan May 27 '15 at 21:49
  • @kahofanfan No worries :). Everyone can edit questions and answer to improve them, like improving the title, so it can be easily found, fixing grammar mistakes in the question text or fixing the code formatting and one can edit the used tags to match the question (like you did with the "erasure" tag). If the edit was done by a user with less then 2000 reputation points, then user with at least 2000 reputation points need to review that edit, before it will be applied. Here is the help page about editing: http://stackoverflow.com/help/editing. – Tom May 27 '15 at 22:05
  • 1
    @PM77-1 Thanks! Here I've got some idea after reading. When I call `readList(list)`, it's actually adding things into list that are 'falsely' regarded as type `ArrayList` so there is no error. But if I do call `println("Type of list = "+list.get(0).getClass());` in `run()` there comes the error. Is it like some criminal escaped from crime scene at first (because he belongs to normal people and normal people has freedom), and when police start to check everyone that was around then he got caught because he is a criminal in deep? – kahofanfan May 27 '15 at 22:37

5 Answers5

6

Java's generics don't actually change the underlying class or object, they just provide (mostly) compile-time semantics around them.

By passing an ArrayList<Integer> into a method expecting an ArrayList (which can hold anything), you're bypassing the compiler's ability to provide you with that type safety.

The Java Generics Tutorial explains this, and why Java implements generics this way. This page, in particular, focusses on it:

Generics were introduced to the Java language to provide tighter type checks at compile time and to support generic programming. To implement generics, the Java compiler applies type erasure to:

  • Replace all type parameters in generic types with their bounds or Object if the type parameters are unbounded. The produced bytecode, therefore, contains only ordinary classes, interfaces, and methods.
  • Insert type casts if necessary to preserve type safety.
  • Generate bridge methods to preserve polymorphism in extended generic types.

Type erasure ensures that no new classes are created for parameterized types; consequently, generics incur no runtime overhead.

What that doesn't say is that this also allows code written with generics (like your run) to interact with code written without generics (like your readList), which is important when adding a feature to a very-well-established language with a huge library base (as they were when adding generics to Java).

T.J. Crowder
  • 879,024
  • 165
  • 1,615
  • 1,639
  • Thanks! Here I've got some idea after reading. When I call `readList(list)`, it's actually adding things into list that are 'falsely' regarded as type `ArrayList` so there is no error. But if I do call `println("Type of list = "+list.get(0).getClass());` in `run()` there comes the error. Is it like some criminal escaped from crime scene at first (because he belongs to normal people and normal people has freedom), and when police start to check everyone that was around then he got caught because he is a criminal in deep? – kahofanfan May 27 '15 at 22:34
  • @kahofanfan: It's because in `run`, the compiler is effectively just automatically adding casting for you. So in `run`, where you have an `ArrayList`, `list.get(0)` effectively becomes `(Integer)list.get(0)`. So if there's a non-`Integer` in the list, because you used an unparameterized reference to the list when adding the string, you get a runtime error from the cast. – T.J. Crowder May 28 '15 at 07:09
3

When you declare:

 private void readList(ArrayList list)

you are not specifying any type to this ArrayList so by default it is of type Object.

Both String and Integer (in fact all classes in java) are sub types of Object. Hence it is possible to add them to list.

For more information about generics without types please read here. In short generics types are for compile time checks only, so that you dont add wrong types (which can later cause exceptions. In this case your operations on String and Integer are compatible so luckily no errors).

Community
  • 1
  • 1
hitz
  • 1,050
  • 8
  • 12
1

In your readList method's parameter you haven't constrained it to only Integer value types, thus list doesn't get the benefits of compile-time checking and must resort to runtime type checking.

neuronaut
  • 2,579
  • 16
  • 23
  • And where do you see a result of the "runtime type checking" in this question? Or does it really happen? – Tom May 27 '15 at 21:31
  • And because of `type erasure` it can't possibly do run-time type checking so it will ultimately manifest by throwing an exception. – PM 77-1 May 27 '15 at 21:32
  • @Tom this question doesn't show the runtime type checking - but it's there. @PM77-1 `type erasure` removes compile-time checking not runtime checking. For example, if you tried to cast the values to int you'd get an exception at runtime for the String value. The exception you mentioned IS runtime type checking. – neuronaut May 27 '15 at 21:36
  • @neuronaut - If you call casting exceptions "run-time checking" I do not envy you users. – PM 77-1 May 27 '15 at 21:38
  • @PM77-1 It's not me calling it that, it's the Java language specification. Nowhere did I say that runtime type checking was a good thing (though it's better than no type checking at all!) But whether its good or not doesn't change that fact that when the JVM internally performs type checking at runtime it will throw a ClassCastException when it detects a type violation. That's what runtime type checking is in the Java world. I do not envy the users of any programmer who doesn't understand that basic principle. – neuronaut May 27 '15 at 21:42
1

Declaration

ArrayList list

in the method readList is equivalent to the

ArrayList<Object> list

It's obvious that String is Object, as well as Integer. When passing to println, both will be toString'ed with their own methods.

basme
  • 81
  • 9
1

I think that the ArrayList type without the generics specification you pass as argument is assument as ArrayList. Both String and Integer inherits Object so they both can be added in the list. However the ArrayList elements are of type Objects.