Является ли кастинг Java параметризованным типом в списках? [Дубликат]

Еще один подход к возврату значения из асинхронной функции - передать объект, который сохранит результат от асинхронной функции.

Вот пример того же:

var async = require("async");

// This wires up result back to the caller
var result = {};
var asyncTasks = [];
asyncTasks.push(function(_callback){
    // some asynchronous operation
    $.ajax({
        url: '...',
        success: function(response) {
            result.response = response;
            _callback();
        }
    });
});

async.parallel(asyncTasks, function(){
    // result is available after performing asynchronous operation
    console.log(result)
    console.log('Done');
});

Я использую объект result для хранения значения во время асинхронной операции. Это позволяет получить результат даже после асинхронного задания.

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

14
задан WiErD0 23 December 2014 в 17:51
поделиться

2 ответа

Генераторы Java реализуются посредством стирания типа, т. е. аргументы типа используются только для компиляции и компоновки, но стираются для выполнения. То есть, нет соответствия 1: 1 между типами времени компиляции и типами времени выполнения. В частности, все экземпляры родового типа используют один и тот же класс среды выполнения:

new ArrayList<Quod>().getClass() == new ArrayList<String>().getClass();

В системе типа времени компиляции аргументы типа присутствуют и используются для проверки типов. В системе типа времени выполнения аргументы типа отсутствуют и поэтому не проверяются.

Это не проблема, а для бросков и необработанных типов. Приведение является утверждением правильности типа и отбрасывает проверку типа от времени компиляции до времени выполнения. Но, как мы видели, нет соответствия 1: 1 между временем компиляции и типами времени выполнения; аргументы типа стираются во время компиляции. Таким образом, среда выполнения не может полностью проверить правильность приведений, содержащих параметры типа, а неправильное выполнение может преуспеть, нарушая систему типа времени компиляции. Спецификация Java Language Specification вызывает это загрязнение кучи .

Как следствие, среда выполнения не может полагаться на правильность аргументов типа. Тем не менее, он должен обеспечить целостность системы типа времени выполнения, чтобы предотвратить повреждение памяти. Это достигается путем задержки проверки типа до тех пор, пока фактическая ссылка не будет использована, и в этот момент среда выполнения знает способ или область, которые она должна поддерживать, и может проверить, что она фактически является экземпляром класса или интерфейса, который объявляет это поле или метод .

С этим вернемся к вашему примеру кода, который я немного упростил (это не меняет поведения):

ArrayList<Quod> test = new ArrayList<Quod>();
ArrayList obj = test; 
obj.add(new Object());
System.out.println(test.get(0));

Объявленный тип obj необработанный тип ArrayList. Необработанные типы отключают проверку аргументов типа во время компиляции. Как следствие, мы можем передать Object в свой метод добавления, хотя ArrayList может содержать только экземпляры Quod в системе типа времени компиляции. То есть мы успешно совладали с компилятором и сделали загрязнение кучи.

Это оставляет систему типа времени выполнения. В системе типа времени выполнения ArrayList работает со ссылками типа Object, поэтому передача Object в метод add в порядке. Так вызывает get(), что также возвращает Object. И вот в чем дело расходится: в вашем первом примере кода у вас есть:

System.out.println(test.get(0));

Тип времени компиляции test.get(0) равен Quod, единственным подходящим методом println является println(Object) и поэтому это подпись метода, встроенная в файл класса. Поэтому во время выполнения мы передаем Object методу println(Object). Это совершенно нормально, и, следовательно, исключение не выбрасывается.

В вашем втором примере кода у вас есть:

System.out.println(test.get(0).toString());

Опять же, тип времени компиляции test.get(0) равен Quod, но теперь мы вызываем его метод toString (). Поэтому компилятор указывает, что метод toString, объявленный в (или унаследованном) типа Quod, должен быть вызван. Очевидно, что этот метод требует, чтобы this указывал на экземпляр Quod, поэтому компилятор добавляет дополнительный бит к Quod в байтовый код перед вызовом метода - и этот бросок бросает ClassCastException.

То есть среда выполнения разрешает первый пример кода, потому что ссылка не используется способом, характерным для Quod, но отклоняет второе, потому что ссылка используется для доступа к методу типа Quod .

Тем не менее, вы не должны полагаться на то, когда именно компилятор будет вставлять этот синтетический прилив, но предотвращать загрязнение кучи в первую очередь, написав правильный код. Java-компиляторы должны помочь вам в этом, выпуская предупреждения и предупреждения необработанного типа, когда ваш код может вызвать загрязнение кучи. Избавьтесь от предупреждений, и вам не нужно будет разбираться в этих деталях; -).

13
ответ дан meriton 27 August 2018 в 20:29
поделиться

Суть вопроса такова:

И почему это прекрасно для метода println () для вызова его toString, но не для меня непосредственно?

ClassCastException исключение не происходит из-за вызова toString(), но из-за явного приведения, добавленного компилятором.

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

Рассмотрим следующий код:

public static void main(String[] args) {
    List<String> s = new ArrayList<String>();
    s.add("kshitiz");
    List<Integer> i = new ArrayList(s);

    System.out.println(i.get(0)); //This works
    System.out.println(i.get(0).toString()); // This blows up!!!
}

Теперь посмотрим на декомпилированный код:

public static void main(String[] args) {
    ArrayList s = new ArrayList();
    s.add("kshitiz");
    ArrayList i = new ArrayList(s);
    System.out.println(i.get(0));
    System.out.println(((Integer)i.get(0)).toString());
}

См. явное приведение к Integer? Теперь почему компилятор не добавил листинг в предыдущей строке? Подпись метода println() такова:

public void println(Object x)

Поскольку println ожидает Object, а результат i.get(0) равен Object, никакой литой не добавляется.

Также вы можете вызвать toString(), если вы сделали это так, чтобы не было создано ни одного акта:

public static void main(String[] args) {
    List<String> s = new ArrayList<String>();
    s.add("kshitiz");
    List<Integer> i = new ArrayList(s);

    myprint(i.get(0));
}

public static void myprint(Object arg) {
    System.out.println(arg.toString()); //Invoked toString but no exception
}
2
ответ дан Kshitiz Sharma 27 August 2018 в 20:29
поделиться
Другие вопросы по тегам:

Похожие вопросы: