массив строк против массива объектов java

#java #types #variadic-functions

#java #типы #переменные-функции

Вопрос:

Я наткнулся на этот раздел прокомментированного кода в StackOverflow. Это вызовет исключение во время выполнения. Вторая часть (раздел без комментариев) будет работать. Я не могу найти разницу между ними, поскольку оба метода возвращают массивы объектов. Возможно, мне не хватает некоторого базового понимания. Не могли бы вы мне помочь?

 public class Safevarargs {

/*  
    static <Object> Object[] asArray(Object... args) {
        return args;
    }

    static <Object> Object[] arrayOfTwo( Object a, Object b) {
        return asArray(a, b);
    }

    public static void main(String[] args) {
        String[] bar = arrayOfTwo("hi", "mom");
    }
*/

    static <Object> Object[] display(Object... args) {
        return args;
    }

    public static void main(String[] args) {
        String[] str =  display("hi", "mom");
        System.out.print(str[0]);
    }
}
  

Комментарии:

1. Что является исключением?

2. Крайне плохой практикой является использование имени, подобного Object для переменной универсального типа.

3. У вас есть какое-то ненужное повторение. Нет никакого способа asArray ошибочным, но нет display .

Ответ №1:

Я полагаю, что у вас возникли проблемы с приведением. Когда вы используете часть

 static <Object> Object[] display(Object... args)
  

на самом деле компилятор видит

 static <Object> Object[] display(Object[]... args)
  

затем он дополнительно преобразует его в это

 static <Object> Object[] display(List[]... args)
  

Это может допускать значения, которых там быть не должно

допустим, вы создаете массив объектов из строки и пытаетесь добавить к нему int. Это может вызвать проблемы с различными типами. Возможно, вы захотите переработать его без использования универсального объекта.

В качестве альтернативы вы могли бы использовать тег @SafeVarargs .

Комментарии:

1. Компилятор видит Object[] при просмотре Object... и ни в коем случае List не задействован.

Ответ №2:

Чтобы объяснить эту проблему, нам просто нужно знать, как работает generic в java. компилятор использует информацию об общем типе внутри в процессе компиляции, генерируя ошибки, связанные с типом, при обработке исходных текстов. Затем, после завершения проверки, компилятор генерирует байтовый код со стертым типом, при этом все ссылки на общие типы заменяются соответствующим стиранием типа.

в вашем втором примере он компилируется в строку во время компиляции. но в вашем первом примере, поскольку asArray вызывается другим универсальным методом, он будет скомпилирован в объект во время компиляции. В результате во время выполнения объект [] не может быть преобразован в String[], что вызовет исключение приведения класса. надеюсь, это может вам помочь

Ответ №3:

Здесь интересная ситуация. В первом случае Java жалуется на преобразование из массива объектов в массив строк.

java.lang.ClassCastException: [Ljava.lang.Объект; не может быть приведен к [Ljava.lang.Строка;

Во втором случае он справляется с этим просто отлично. Проблема, похоже, заключается в стирании типа. При asArray возврате он возвращает массив, типизированный на основе его параметров. Эта информация стирается в контексте arrayOfTwo , где тип просто становится Object[] , и тот факт, что содержимое является строками, больше не доступен.

Взгляните на этот пост об удалении типов и невоспроизводимых типах, а также о последствиях для переменных. Он предоставляет некоторый контекст для предупреждения, которое генерирует код, о «возможном загрязнении кучи»:

Safevarargs.java:11: предупреждение: [снято] Возможное загрязнение кучи от параметризованного объекта типа vararg
статический объект[] отображение (объект… аргументы) {
^ где объект — переменная типа:
объект расширяет java.lang.Объект, объявленный в методе display(Object …)

Из документации:

Загрязнение кучи происходит, когда переменная параметризованного типа ссылается на объект, который не относится к этому параметризованному типу.