20

I am having trouble understanding what is actually happening when I declare the array in the following code.

class Type1 {

}

class Type2 extends Type1 {
    public void method2() {

    }
}

class Test {
    public static void main(String[] args) {

        Type1[] x = new Type2[2];
        x[0] = new Type1(); // runtime error
        x[1] = new Type2();
        x[1].method2(); // syntax error

    }
}

I thought since the right-hand side of the array declaration is new Type2[2] that the array would consist of reference variables of the type Type2. If this is true, the first error makes sense because I cannot have a subtype referring to a supertype.

However, why does the second error occur two lines after that? Isn't method2() known by Type2, so the method is known by the reference variable? It seems it is because Type1 doesn't know method2, so does that mean the array consists of reference variables of the type Type1? If this is true, why does the first error occur as it is no longer a subtype referring to a supertype?

Also, why is the first error a runtime error while the other is a syntax error?

Please note I am only in my second programming course, so my terminology may be a little off.

Edit: The question here does not answer my question because it does not answer why an element of an array like x cannot invoke method2() even though an element of x of Type 2. My question is different because of this and because my question also asks why the first error occurs when the second error also occurs (why an element of x cannot refer to and object of the type Type1 and at the same time cannot invoke method2()). I originally thought that if one error occurred, then the other cannot occur. I wanted a comparison between the two errors and a more in-depth explanation than simply the rules of polymorphism.

Lindstorm
  • 890
  • 2
  • 7
  • 22
  • 4
    For a beginner, this is a very good question indeed. – T.J. Crowder Feb 17 '18 at 09:58
  • The second error is somewhat unrelated to the first and arrays - you'd get the same error with the non-array equivalent - `Type1 x = new Type2(); x.method2();` – Bernhard Barker Feb 17 '18 at 11:03
  • Your question is answered in [ArrayStoreException when trying to set an element of an array to a superclass object](https://stackoverflow.com/q/16914132) and [Why do I get a compile-time error when calling a subclass method using super class reference?](https://stackoverflow.com/q/37853463) – Bernhard Barker Feb 17 '18 at 11:17
  • Related: [Call a method of subclass in Java](https://stackoverflow.com/q/2701182) – Bernhard Barker Feb 17 '18 at 11:22

3 Answers3

15

This is one of those weird things that Java allows you to do, assigning an array of a derived class to a variable of array of the base class.

In your code, x at compile time is of type Type1[]. That's what the compiler thinks it is. At runtime, x is of type Type2[], but the compiler does not know that.

The first error occurs at runtime because as you said, you can't assign Type1 to a variable of type Type2.

But the second error occurs at compile time because the compiler still thinks that x is of type Type1, and there is no method called method2 in Type1, even though x actually holds a Type2[] at runtime.

To call method2, you need to tell the compiler that x[1] is of type Type2 by casting:

((Type2)x[1]).method2();

Lesson of the day? Don't do this:

Superclass[] array = new Subclass[2];

You'll get yourself in trouble.

Vince
  • 14,470
  • 7
  • 39
  • 84
Sweeper
  • 213,210
  • 22
  • 193
  • 313
6

A super-type reference can refer to a sub-type. I think you understand this, since the first error is coherent to you.

The second error stems from the fact that at compile time, x is still a Type1[]. This means that at run-time the reference can hold any sub-type, including a type that doesn't have the method method2. That's why you can only use the methods defined in Type1.

You could, for example, check whether the type is actually Type2 at runtime using isInstanceOf, and then cast it to a Type2 and then use method2. There are usually better solutions, though.

Kraylog
  • 7,383
  • 1
  • 24
  • 35
0

to put it in layman terms for the below code,

Type1[] x = new Type2[2];

let us just take Type1 x = new Type2; not arrays, just classes.

What do you infer from the above step?

Here x is a reference variable. We are creating an object of "Type2" and x has the reference of the Object Type2 [lets just say, x is a remote control pointing to Type2 object]. Dont forget that, x is of the type 'Type1', which means it does not have a 'method2()' button on its remote control.[Remember, method2 is of Type2 Class not Type1].

Compiler calls a method on an object,only when it sees it inside the class. compiler does not care which Object you are creating [here: new Type2], all it cares is on whom you are making the call (here: x[1], which is of type Type1). So, it throws the compile time error. It does not bother what happens at runtime or which object it is pointing to, all it cares is if Whether the reference type has the calling method or not [In Our terminology, does the remote control has that button]?.

why is the first error a runtime error while the other is a syntax error?

I hope the above explanation, (when you consider Array types as per your question) answers your second half of the question.

And, as for the first part, you pretty much answered it. Anyhow, thing is Arrays allow this kind of stuff and blows up at Runtime whereas, Arraylist do it at compile time.[That is a part of Generics].

As you said,you are new to this stuff, here is my reference for You.

Reference:

Head First Java

Jayanth
  • 746
  • 6
  • 17