Общее приведение Java, непрямое

Я нашел что-то странное в приведении типов в Java, я никогда раньше этого не видел. Приведение на самом деле не выполняется там, где вы запрограммировали его в общем методе.

Тестируем странную вещь.

На хэш-карте:

HashMap<String,Object> map = ...
map.put("hello", "World");
System.err.println((Integer)map.get("hello")); //  -----> ClassCastException

На карте Обертка

MapWrap wrap = ...
wrap.put("hello", "World");
System.err.println(wrap.get("hello",Integer.class)); // -----> don't cast, print World (i guess because println receives an Object reference but the cast should be done before that).
System.err.println(wrap.get("hello", Integer.class).toString()); // -----> print World + ClassCastException

Код методов:

private <T> T get(String key, Class<T> c){
    return (T)map.get(key);
}

private Object get(String key){
    return map.get(key);
}

Кто-нибудь знает, есть ли у этого механизма имя или что-то о нем известно?

Спасибо


person kazgul    schedule 26.01.2012    source источник


Ответы (3)


В ролях:

(T) map.get(key);

вообще ничего не делает из-за стирания типа. Метод MapWrap.get() будет стерт до:

private Object get(String key, Class<T> c){
    return map.get(key);
}

который всегда будет работать. Приведение к Integer будет вставлено только там, где вы присваиваете результат этого метода, и поскольку в первом примере MapWrap вы передаете его методу, который ожидает параметр Object, этого не происходит.

Во втором случае вы пытаетесь вызвать метод Integer.toString(), поэтому приведение к Integer вставляется и терпит неудачу.

Вы уже передаете объект класса, правильный способ сделать "общее приведение" таков:

private <T> T get(String key, Class<T> c){
    return c.cast(map.get(key));
}
person millimoose    schedule 26.01.2012

Параметры типов стираются при компиляции. Таким образом, приведение, такое как (T), становится (Object) в исполняемом файле, следовательно, первое поведение, которое вы получаете.

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

Однако во второй строке, я думаю, компилятор генерирует вызов метода Integer.toString(), поэтому требуется приведение, отсюда и исключение.

См. раздел Введите Erasure в руководстве по Java.

person ChrisJ    schedule 26.01.2012

Я бы назвал это: «шаблоны в Java — это боль» ;-).

Линия :

System.err.println(wrap.get("hello",Integer.class));

Использует метод println(Object obj), поэтому проблем с типом нет, так как на самом деле никто не проверяет тип возвращаемого объекта для wrap.get

Во втором случае:

System.err.println(wrap.get("hello", Integer.class).toString());

Вызывается toString() для Integer, и вы получили исключение приведения класса.

К сожалению, как указывает Крис, Java стирает тип из шаблона, но помните, что он все еще существует в методах (возможно)

person ic3    schedule 26.01.2012
comment
Вы также можете назвать это дженериками Java, это не что иное, как шаблоны C++;) Серьезно, лучше не думать о дженериках в терминах C++. Они действительно принципиально совсем другие. - person Cameron Skinner; 26.01.2012
comment
Я знаю, но это настоящая боль, когда вы пытаетесь сделать что-то большее, чем базовое... и, кроме того, у нас нет поддержки базовых типов (int, float). Отлично подходит для перформанса - person ic3; 26.01.2012
comment
Дженерики @icCube Java на самом деле несколько переработаны для того, что они должны включать. Что делает существующий API коллекций и аналогичные варианты использования типобезопасными. А поскольку коллекции Java не могли эффективно обрабатывать примитивные типы до появления дженериков, производительность не терялась. (Из-за стирания типов их также нельзя было заставить эффективно с ними справляться. .NET предпочла использовать реификацию вместо стирания типов, но цена заключалась в создании нового API-интерфейса коллекций, а также в том, что многие устаревшие API-интерфейсы, как и большинство ASP.NET — не были и не могут быть обновлены для использования дженериков.) - person millimoose; 27.01.2012
comment
Например, собираетесь ли вы реализовать алгоритм бинарного поиска или другие алгоритмы для каждого базового типа? это то, что мы делаем в настоящее время ... да, иногда мы хотим, чтобы виртуальная машина создавала новый код (да, что-то вроде старого макроса). У Java много замечательных идей, но они ооочень упрямы и выглядят так, будто никогда серьезно не относятся к полевым математическим задачам. - person ic3; 27.01.2012