Начальный массив объектов Путаница
У меня возникли проблемы с пониманием того, что на самом деле происходит, когда я объявляю массив в следующем коде.
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
}
}
Я думал, поскольку правая часть объявления массива является new Type2[2]
что массив будет состоять из ссылочных переменных типа Type2
. Если это так, первая ошибка имеет смысл, потому что у меня нет подтипа, относящегося к супертипу.
Однако почему вторая ошибка возникает после двух строк? Разве method2()
известен по типу 2, поэтому метод известен ссылочной переменной? Кажется, это потому, что Type1
не знает method2
, значит, это означает, что массив состоит из ссылочных переменных типа Type1
? Если это так, то почему возникает первая ошибка, поскольку она больше не является подтипом, относящимся к супертипу?
Кроме того, почему первая ошибка является ошибкой во время выполнения, а другая - синтаксической ошибкой?
Пожалуйста, обратите внимание, что я только на своем втором курсе программирования, поэтому моя терминология может быть немного отключена.
Изменение: вопрос здесь не отвечает на мой вопрос, потому что он не отвечает, почему элемент массива типа x
не может вызвать method2()
хотя элемент x
Type 2
. Мой вопрос отличается из-за этого и потому, что в моем вопросе также спрашивается, почему первая ошибка возникает при возникновении второй ошибки (почему элемент x
не может ссылаться и объект типа Type1
и в то же время не может вызвать method2()
), Первоначально я думал, что если произошла одна ошибка, другая не может произойти. Я хотел сравнить между двумя ошибками и более подробное объяснение, чем просто правила полиморфизма.
Ответы
Ответ 1
Это одна из тех странных вещей, которые позволяет Java, назначая массив производного класса переменной массива базового класса.
В вашем коде x
во время компиляции имеет тип Type1[]
. Это то, что думает компилятор. Во время выполнения x
имеет тип Type2[]
, но компилятор этого не знает.
Первая ошибка возникает во время выполнения, поскольку, как вы сказали, вы не можете назначать Type1
переменной типа Type2
.
Но вторая ошибка возникает во время компиляции, потому что компилятор все еще считает, что x
имеет тип Type1
, и нет метода под названием method2
в Type1
, хотя x
фактически содержит Type2[]
во время выполнения.
Чтобы вызвать method2
, вам нужно сообщить компилятору, что x[1]
имеет тип Type2
путем кастинга:
((Type2)x[1]).method2();
Урок дня? Не делайте этого:
Superclass[] array = new Subclass[2];
Вы попадете в беду.
Ответ 2
Ссылка на супертип может относиться к подтипу. Я думаю, вы понимаете это, поскольку первая ошибка является последовательной для вас.
Вторая ошибка связана с тем, что во время компиляции x
по-прежнему является Type1[]
. Это означает, что во время выполнения ссылка может содержать любой подтип, включая тип, который не имеет метода method2
. Вот почему вы можете использовать только методы, определенные в Type1
.
Например, вы можете проверить, действительно ли тип является Type2
во время выполнения, используя isInstanceOf
, а затем отбрасывает его в Type2
и затем использует method2
. Однако, как правило, есть лучшие решения.
Ответ 3
чтобы поставить его в непрофессионалы для нижеприведенного кода,
Type1[] x = new Type2[2];
давайте просто возьмем Type1 x = new Type2;
а не массивы, просто классы.
Что вы делаете на этом этапе?
Здесь x является ссылочной переменной. Мы создаем объект "Type2", и x имеет ссылку Object Type2 [позволяет просто сказать, x - это удаленный элемент управления, указывающий на объект Type2). Не забывайте, что x имеет тип "Type1", что означает, что на пульте дистанционного управления нет кнопки "method2()". [Помните, метод2 относится к классу Type2 не Type1].
Компилятор вызывает метод для объекта, только когда он видит его внутри класса. компилятор не заботится о том, какой объект вы создаете [здесь: новый Type2], все, что он заботит, это то, на кого вы делаете вызов (здесь: x [1], который имеет тип Type1). Таким образом, он выдает ошибку времени компиляции. Он не беспокоится о том, что происходит во время выполнения или на каком объекте он указывает, все, что ему нужно, - это если у ссылочного типа есть метод вызова или нет. [В нашей терминологии, имеет ли пульт ДУ эта кнопка]? ,
почему первая ошибка является ошибкой во время выполнения, а другая - синтаксической ошибкой?
Я надеюсь, что приведенное выше объяснение (когда вы рассматриваете типы Array по вашему вопросу) отвечает на вашу вторую половину вопроса.
И, что касается первой части, вы в значительной степени ответили на нее. Как бы то ни было, дело в том, что массивы допускают такие вещи и взрываются в Runtime, тогда как Arraylist делает это во время компиляции. [Это часть Generics].
Как вы сказали, вы новичок в этом, вот моя ссылка для вас.
Ссылка:
Первая глава Java