2

I was doing a reading on how child class objects can be assigned to parent class containers. There are 3 scenarios, listed below. For collections, arrays and plain objects. I got confused between the three scenarios about how & why all three are handled differently in Java. Is there a simple explanation for below scenarios, please? They seem to be very basic java concepts, don't know why are confusing me.

  1. This gives compile time error.

    List<Object> objectList = new ArrayList<Integer>();//compile time error
    
  2. But, this is allowed

    Object object = new Integer(9);
    object = 1.2;// no run time error
    
  3. But this is not? (though, technically compiler allows but we get a runtime error)

    Object objectArr[] = new Integer[1];
    objectArr[0] = 1.2;// run time error (Exception in thread "main" java.lang.ArrayStoreException: java.lang.Double)
    
Lajos Arpad
  • 64,414
  • 37
  • 100
  • 175
Kumar Manish
  • 1,166
  • 1
  • 16
  • 28

3 Answers3

3

You can assign something of type A to a variable of type B if A is a subtype of B. Broadly speaking, for non-primitive types, this means that A must support all the operations that B does.

Looking at your examples:

First:

List<Object> objectList = new ArrayList<Integer>();//compile time error

Right, because an ArrayList<Integer> is not a List<Object>; you cannot add an Object to it. Consider:

List<Object> l = new ArrayList<Object>();
List<Integer> l2 = new ArrayList<Integer>();
l.add(new Object()); // ok
l2.add(new Object()); // not ok; `List<Integer>` doesn't support this.

So you see a List<Integer> (or an ArrayList<Integer>) is not a subtype of List<Object> - because it doesn't support all the same operations.

Next:

Object object = new Integer(9);
object = 1.2;// no run time error

In this case, the value 1.2 is auto-boxed into the wrapper type Double (full name java.lang.Double). As this is a subclass of Object the assignment then works fine. What is stored in the variable object is not the primitive double value 1.2, but rather a reference to a Double object which wraps the primitive value.

Finally:

Object objectArr[] = new Integer[1];
objectArr[0] = 1.2;// run time error (Exception in thread "main" java.lang.ArrayStoreException: java.lang.Double)

This is the oddball case. Java considers an array of Integer to be a subtype of an array of Object, though by normal type theory this wouldn't be the case, since you can't store an Object (that isn't also an Integer) into such an array. On the other hand, an array of Integer does at least support all the other operations that an array of Object does - you can retrieve elements and be sure they are a subtype of Object; you can check the array length; etc. So, you can assign new Integer[1] - an Integer array - to a variable of type Object[].

To make this form of sub-typing work, the Integer array needs to implement a "store Object element" operation similar to what Object[] implicitly supports. Since an Integer[] is only supposed to contain Integer objects, however, the store operation must fail - so you get an exception at runtime if you try to store something that isn't an Integer. In your example you're storing 1.2 which is auto-boxed to a Double, not an Integer.

davmac
  • 20,150
  • 1
  • 40
  • 68
  • Why "object = 1.2" is boxing but "objectArr[0] = 1.2" not? – Kumar Manish Aug 27 '16 at 09:44
  • @KumarManish it _is_ also boxing. But it boxes to the `Double` type, and the array is an `Integer` array; a `Double` is not an `Integer`, so you can't store one in the array. – davmac Aug 27 '16 at 10:00
  • Array is an integer array so can't push in Double. Understood. But the same doesn't hold good for object when we do "Object object = new Integer(9)" ? How can we store double in "object" which is initialized with "new Integer(9)" , then? Something related to how "new Integer(9)" gets created in memory heap vs "new Integer[1]" ? – Kumar Manish Aug 27 '16 at 10:26
  • @KumarManish Reference-type variables in Java are references. You are initialising the `object` variable with a reference to an `Integer` and then replacing it with a reference to a `Double`. In the case of the array you cannot replace an element value with an object of an incompatible type. A variable's dynamic type changes to whatever you assign to it, but eg the `object` variable always retains the _static_ type of `Object`. An array element's static type also cannot be changed. – davmac Aug 27 '16 at 18:32
1
  1. ArrayList<Integer> is not an implementation of List<Object>. ArrayList<Object> is.

  2. You create an Integer and assign it to an Object variable. You cannot assign a double to an Object, since double is a primitive type and does not inherit from Object. You could assign a Double though.

  3. You create an Integer array of a single element and assign it to a variable of Object array. Then you try to assign a value to your Integer element, which is not an Integer.

EDIT:

As davman explained, my answer on 2. is incorrect, object = 1.2; is actually valid code due to boxing.

Further explanation:

  1. Any Integer is an Object, but an Object is not necessarily an Integer. You can expect to be able to add any Object into a List<Object>, but you can add only Integer values into an ArrayList<Integer>. Now, if you assign an ArrayList<Integer> to a List<Object>, then later you might try to add a Double to List<Object>, for instance, which is perfectly valid with List<Object>. But since you stored an ArrayList<Integer>, where the operation is invalid, the operation would fail. This failure is due to the inconsistency of assigning ArrayList<Integer> to List<Object>. The compiler defends you from this inconsistency with the compile time error.

  2. Even though you have an Object array, that does not change the fact that you have an Integer inside it. And you try to assign a value to that Integer, which is invalid to Integers.

Community
  • 1
  • 1
Lajos Arpad
  • 64,414
  • 37
  • 100
  • 175
  • 1
    Your answer for (1) is good but could do with some added explanation (why is `ArrayList` not a `List` - what operations doesn't it support)? Your answer for (2) is incorrect because you don't take boxing into account. For (3) again a little more detail would be nice. – davmac Aug 27 '16 at 08:45
  • @davmac, thank you for the constructive criticism. I will edit my answer accordingly. – Lajos Arpad Aug 27 '16 at 08:52
0

In your first line you must write:

List<? extends Object> objectList = new ArrayList<Integer>();

then it works. ? extends Object means the List contains elements which extends the Object, e.g. Integer.

if you use the ? operator as given here, then you do not have an add method anymore. So you need first to add all the elements in your ArrayList and assign it later:

ArrayList<Integer> al = new ArrayList<>();
al.add(3);
...
List<? extends Object> objectList = al;