Что такое сырой тип и почему мы не должны его использовать?
Вопросы:
- Что такое необработанные типы в Java, и почему я часто слышу, что они не должны использоваться в новом коде?
- Какая альтернатива, если мы не можем использовать необработанные типы, и как это лучше?
16 ответов
Что такое сырой тип?
Спецификация языка Java определяет необработанный тип следующим образом:
JLS 4.8 Необработанные типы
Необработанный тип определяется как один из:
Тип ссылки, который формируется путем взятия имени объявления универсального типа без сопровождающего списка аргументов типа.
Тип массива, тип элемента которого является необработанным типом.
Не
static
тип члена необработанного типаR
который не унаследован от суперкласса или суперинтерфейсаR
,
Вот пример для иллюстрации:
public class MyType<E> {
class Inner { }
static class Nested { }
public static void main(String[] args) {
MyType mt; // warning: MyType is a raw type
MyType.Inner inn; // warning: MyType.Inner is a raw type
MyType.Nested nest; // no warning: not parameterized type
MyType<Object> mt1; // no warning: type parameter given
MyType<?> mt2; // no warning: type parameter given (wildcard OK!)
}
}
Вот, MyType<E>
является параметризованным типом ( JLS 4.5). Обычно в разговорной речи называют этот тип просто MyType
для краткости, но технически это имя MyType<E>
,
mt
имеет необработанный тип (и генерирует предупреждение о компиляции) по первому пункту в приведенном выше определении; inn
также имеет необработанный тип по третьему пункту.
MyType.Nested
не параметризованный тип, даже если это тип члена параметризованного типа MyType<E>
, потому что это static
,
mt1
, а также mt2
оба объявлены с фактическими параметрами типа, поэтому они не являются необработанными типами.
Что такого особенного в необработанных типах?
По сути, необработанные типы ведут себя так же, как и до появления дженериков. То есть следующее абсолютно законно во время компиляции.
List names = new ArrayList(); // warning: raw type!
names.add("John");
names.add("Mary");
names.add(Boolean.FALSE); // not a compilation error!
Приведенный выше код работает просто отлично, но предположим, что у вас также есть следующее:
for (Object o : names) {
String name = (String) o;
System.out.println(name);
} // throws ClassCastException!
// java.lang.Boolean cannot be cast to java.lang.String
Теперь мы сталкиваемся с проблемами во время выполнения, потому что names
содержит что-то, что не является instanceof String
,
Предположительно, если вы хотите names
содержать только String
возможно, вы все еще можете использовать необработанный тип и вручную проверять каждый add
себя, а затем вручную привести к String
каждый предмет из names
, Еще лучше, хотя НЕ использовать необработанный тип и позволить компилятору сделать всю работу за вас, используя всю мощь Java-обобщений.
List<String> names = new ArrayList<String>();
names.add("John");
names.add("Mary");
names.add(Boolean.FALSE); // compilation error!
Конечно, если вы хотите names
разрешить Boolean
тогда вы можете объявить это как List<Object> names
и приведенный выше код будет скомпилирован.
Смотрите также
Чем необработанный тип отличается от использования <Object>
как параметры типа?
Ниже приводится цитата из Effective Java 2nd Edition, пункт 23. Не используйте необработанные типы в новом коде:
Какова разница между необработанным типом
List
и параметризованный типList<Object>
? Грубо говоря, первый отказался от проверки общего типа, а второй явно сказал компилятору, что он способен содержать объекты любого типа. Хотя вы можете передатьList<String>
к параметру типаList
, вы не можете передать его параметру типаList<Object>
, Существуют правила подтипов для дженериков, иList<String>
это подтип необработанного типаList
, но не параметризованного типаList<Object>
, Как следствие, вы теряете безопасность типов, если вы используете сырой тип какList
, но не если вы используете параметризованный тип какList<Object>
,
Чтобы проиллюстрировать это, рассмотрим следующий метод, который принимает List<Object>
и добавляет new Object()
,
void appendNewObject(List<Object> list) {
list.add(new Object());
}
Обобщения в Java инвариантны. List<String>
это не List<Object>
, поэтому следующее генерирует предупреждение компилятора:
List<String> names = new ArrayList<String>();
appendNewObject(names); // compilation error!
Если бы вы объявили appendNewObject
взять сырой тип List
в качестве параметра, то это будет скомпилировано, и вы потеряете безопасность типов, которую вы получаете от дженериков.
Смотрите также
- В чем разница между
<E extends Number>
а также<Number>
? - java дженерики (не) ковариация
Чем необработанный тип отличается от использования <?>
как параметр типа?
List<Object>
, List<String>
и т. д. все List<?>
так что может быть заманчиво просто сказать, что они просто List
вместо. Тем не менее, есть большая разница: так как List<E>
определяет только add(E)
, вы не можете добавить какой-либо произвольный объект к List<?>
, С другой стороны, так как сырой тип List
не имеет тип безопасности, вы можете add
почти что-нибудь для List
,
Рассмотрим следующий вариант предыдущего фрагмента:
static void appendNewObject(List<?> list) {
list.add(new Object()); // compilation error!
}
//...
List<String> names = new ArrayList<String>();
appendNewObject(names); // this part is fine!
Компилятор прекрасно защитил вас от возможного нарушения инвариантности типов List<?>
! Если вы объявили параметр как необработанный тип List list
то код скомпилируется, и вы нарушите инвариант типа List<String> names
,
Необработанный тип - это стирание этого типа.
Вернуться к JLS 4.8:
В качестве типа можно использовать стирание параметризованного типа или стирание типа массива, тип элемента которого является параметризованным типом. Такой тип называется необработанным типом.
[...]
Суперклассы (соответственно, суперинтерфейсы) необработанного типа являются стиранием суперклассов (суперинтерфейсов) любой из параметризаций универсального типа.
Тип конструктора, метод экземпляра или не
static
поле необработанного типаC
который не унаследован от своих суперклассов или суперинтерфейсов, является необработанным типом, который соответствует стиранию его типа в общем объявлении, соответствующемC
,
Проще говоря, когда используется необработанный тип, конструкторы, методы экземпляра и не static
поля также стираются.
Возьмите следующий пример:
class MyType<E> {
List<String> getNames() {
return Arrays.asList("John", "Mary");
}
public static void main(String[] args) {
MyType rawType = new MyType();
// unchecked warning!
// required: List<String> found: List
List<String> names = rawType.getNames();
// compilation error!
// incompatible types: Object cannot be converted to String
for (String str : rawType.getNames())
System.out.print(str);
}
}
Когда мы используем сырье MyType
, getNames
стирается, так что он возвращает сырой List
!
JLS 4.6 продолжает объяснять следующее:
Стирание типа также отображает сигнатуру конструктора или метода в сигнатуру, которая не имеет параметризованных типов или переменных типа. Стирание сигнатуры конструктора или метода
s
подпись, состоящая из того же имени, что иs
и стирания всех типов формальных параметров, приведенных вs
,Возвращаемый тип метода и параметры типа универсального метода или конструктора также стираются, если сигнатура метода или конструктора удалена.
Удаление подписи универсального метода не имеет параметров типа.
В следующем отчете об ошибках содержатся некоторые соображения Маурицио Симадамора, разработчика компилятора, и Алекса Бакли, одного из авторов JLS, о причинах такого поведения: https://bugs.openjdk.java.net/browse/JDK-6400189. (Короче говоря, это упрощает спецификацию.)
Если это небезопасно, почему разрешено использовать необработанный тип?
Вот еще одна цитата из JLS 4.8:
Использование необработанных типов допускается только в качестве уступки совместимости устаревшего кода. Использование необработанных типов в коде, написанном после введения универсальности в язык программирования Java, настоятельно не рекомендуется. Вполне возможно, что будущие версии языка программирования Java будут запрещать использование необработанных типов.
Effective Java 2nd Edition также имеет следующее:
Учитывая, что вы не должны использовать необработанные типы, почему разработчики языка разрешили это? Для обеспечения совместимости.
Платформа Java собиралась вступить во второе десятилетие, когда были представлены дженерики, и существовало огромное количество Java-кода, который не использовал дженерики. Было решено, что весь этот код остается легальным и совместимым с новым кодом, который использует дженерики. Должно быть допустимо передавать экземпляры параметризованных типов в методы, которые были разработаны для использования с обычными типами, и наоборот. Это требование, известное как совместимость миграции, привело к принятию решения о поддержке необработанных типов.
Таким образом, необработанные типы никогда не должны использоваться в новом коде. Вы должны всегда использовать параметризованные типы.
Есть ли исключения?
К сожалению, поскольку дженерики Java не являются reified, есть два исключения, где необработанные типы должны использоваться в новом коде:
- Литералы класса, например
List.class
неList<String>.class
instanceof
операнд, напримерo instanceof Set
неo instanceof Set<String>
Смотрите также
- Почему
Collection<String>.class
Нелегальная?
Что такое необработанные типы в Java, и почему я часто слышу, что они не должны использоваться в новом коде?
Raw-типы - это древняя история языка Java. В начале были Collections
и они держали Objects
Не больше, не меньше. Каждая операция на Collections
необходимые броски из Object
до желаемого типа.
List aList = new ArrayList();
String s = "Hello World!";
aList.add(s);
String c = (String)aList.get(0);
Хотя это работало большую часть времени, ошибки все же случались
List aNumberList = new ArrayList();
String one = "1";//Number one
aNumberList.add(one);
Integer iOne = (Integer)aNumberList.get(0);//Insert ClassCastException here
Старые коллекции без типов не могли обеспечить безопасность типов, поэтому программист должен был помнить, что он хранит в коллекции.
Обобщения, которые были изобретены, чтобы обойти это ограничение, разработчик объявлял бы сохраненный тип один раз, и компилятор сделал бы это вместо этого.
List<String> aNumberList = new ArrayList<String>();
aNumberList.add("one");
Integer iOne = aNumberList.get(0);//Compile time error
String sOne = aNumberList.get(0);//works fine
Для сравнения:
// Old style collections now known as raw types
List aList = new ArrayList(); //Could contain anything
// New style collections with Generics
List<String> aList = new ArrayList<String>(); //Contains only Strings
Более сложный сравниваемый интерфейс:
//raw, not type save can compare with Other classes
class MyCompareAble implements CompareAble
{
int id;
public int compareTo(Object other)
{return this.id - ((MyCompareAble)other).id;}
}
//Generic
class MyCompareAble implements CompareAble<MyCompareAble>
{
int id;
public int compareTo(MyCompareAble other)
{return this.id - other.id;}
}
Обратите внимание, что невозможно реализовать CompareAble
интерфейс с compareTo(MyCompareAble)
с необработанными типами. Почему вы не должны использовать их:
- любой
Object
хранится вCollection
должен быть брошен, прежде чем его можно будет использовать - Использование обобщений позволяет проверять время компиляции
- Использование необработанных типов аналогично сохранению каждого значения
Object
Что делает компилятор: Generics обратно совместимы, они используют те же классы Java, что и необработанные типы. Магия происходит в основном во время компиляции.
List<String> someStrings = new ArrayList<String>();
someStrings.add("one");
String one = someStrings.get(0);
Будет скомпилировано как:
List someStrings = new ArrayList();
someStrings.add("one");
String one = (String)someStrings.get(0);
Это тот же код, который вы написали бы, если бы использовали непосредственные типы. Думаю, я не уверен, что происходит с CompareAble
интерфейс, я думаю, что это создает два compareTo
функции, один принимая MyCompareAble
а другой берет Object
и передать его первому после его наложения.
Какие альтернативы необработанным типам: Используйте дженерики
Необработанный тип - это имя универсального класса или интерфейса без аргументов типа. Например, учитывая общий класс Box:
public class Box<T> {
public void set(T t) { /* ... */ }
// ...
}
Чтобы создать параметризованный тип Box<T>
, вы предоставляете фактический аргумент типа для формального параметра типа T
:
Box<Integer> intBox = new Box<>();
Если фактический аргумент типа опущен, вы создаете необработанный тип Box<T>
:
Box rawBox = new Box();
Следовательно, Box
это необработанный тип универсального типа Box<T>
, Однако неуниверсальный класс или тип интерфейса не является необработанным типом.
Необработанные типы отображаются в устаревшем коде, потому что многие классы API (такие как классы Collections) не были универсальными до JDK 5.0. При использовании необработанных типов вы, по сути, получаете пре-родовое поведение - Box
дает тебе Object
s. Для обратной совместимости допускается присвоение параметризованного типа его необработанному типу:
Box<String> stringBox = new Box<>();
Box rawBox = stringBox; // OK
Но если вы назначите необработанный тип параметризованному типу, вы получите предупреждение:
Box rawBox = new Box(); // rawBox is a raw type of Box<T>
Box<Integer> intBox = rawBox; // warning: unchecked conversion
Вы также получите предупреждение, если используете необработанный тип для вызова универсальных методов, определенных в соответствующем универсальном типе:
Box<String> stringBox = new Box<>();
Box rawBox = stringBox;
rawBox.set(8); // warning: unchecked invocation to set(T)
Предупреждение показывает, что необработанные типы обходят проверки универсальных типов, откладывая перехват небезопасного кода до времени выполнения. Поэтому вам следует избегать использования необработанных типов.
Раздел Erasure Type содержит больше информации о том, как компилятор Java использует необработанные типы.
Непроверенные сообщения об ошибках
Как упоминалось ранее, при смешивании устаревшего кода с универсальным кодом вы можете встретить предупреждающие сообщения, подобные следующим:
Примечание: Example.java использует непроверенные или небезопасные операции.
Примечание: Перекомпилируйте с -Xlint: не проверено для деталей.
Это может произойти при использовании старого API, который работает с необработанными типами, как показано в следующем примере:
public class WarningDemo {
public static void main(String[] args){
Box<Integer> bi;
bi = createBox();
}
static Box createBox(){
return new Box();
}
}
Термин "непроверенный" означает, что у компилятора недостаточно информации о типе, чтобы выполнить все проверки типов, необходимые для обеспечения безопасности типов. По умолчанию предупреждение "unchecked" отключено, хотя компилятор дает подсказку. Чтобы увидеть все "непроверенные" предупреждения, перекомпилируйте с -Xlint:unchecked.
Перекомпиляция предыдущего примера с -Xlint: unchecked открывает следующую дополнительную информацию:
WarningDemo.java:4: warning: [unchecked] unchecked conversion
found : Box
required: Box<java.lang.Integer>
bi = createBox();
^
1 warning
Чтобы полностью отключить непроверенные предупреждения, используйте флаг -Xlint:-unchecked. @SuppressWarnings("unchecked")
аннотация подавляет непроверенные предупреждения. Если вы не знакомы с @SuppressWarnings
синтаксис, см. аннотации.
Первоначальный источник: Java Tutorials
"Необработанный" тип в Java - это класс, который не является универсальным и имеет дело с "необработанными" объектами, а не с типизированными параметрами универсального типа.
Например, до того как Java-дженерики стали доступны, вы должны использовать класс коллекции следующим образом:
LinkedList list = new LinkedList();
list.add(new MyObject());
MyObject myObject = (MyObject)list.get(0);
Когда вы добавляете свой объект в список, его не волнует, какой это тип объекта, и когда вы получаете его из списка, вы должны явно привести его к ожидаемому типу.
Используя дженерики, вы удаляете "неизвестный" фактор, потому что вы должны явно указать, какой тип объектов может идти в списке:
LinkedList<MyObject> list = new LinkedList<MyObject>();
list.add(new MyObject());
MyObject myObject = list.get(0);
Обратите внимание, что при использовании дженериков вам не нужно приводить объект, полученный из вызова get, коллекция предопределена для работы только с MyObject. Этот факт является основным движущим фактором для дженериков. Он изменяет источник ошибок времени выполнения на что-то, что можно проверить во время компиляции.
private static List<String> list = new ArrayList<String>();
Вы должны указать тип-параметра.
Предупреждение предупреждает, что типы, которые определены для поддержки обобщений, должны быть параметризованы, а не использовать их необработанную форму.
List
определяется для поддержки обобщений: public class List<E>
, Это позволяет выполнять многие безопасные для типов операции, которые проверяются во время компиляции.
Что такое необработанный тип и почему я часто слышу, что они не должны использоваться в новом коде?
"Необработанный тип" - это использование универсального класса без указания аргумента (ов) типа для его параметризованного типа (типов), например, с использованием List
вместо List<String>
, Когда дженерики были введены в Java, несколько классов были обновлены для использования дженериков. Использование этих классов в качестве "необработанного типа" (без указания аргумента типа) позволило устаревшему коду все еще компилироваться.
"Необработанные типы" используются для обратной совместимости. Их использование в новом коде не рекомендуется, поскольку использование универсального класса с аргументом типа обеспечивает более строгую типизацию, что, в свою очередь, может улучшить понимание кода и привести к выявлению потенциальных проблем ранее.
Какая альтернатива, если мы не можем использовать необработанные типы, и как это лучше?
Предпочтительной альтернативой является использование универсальных классов по назначению - с подходящим аргументом типа (например, List<String>
). Это позволяет программисту более конкретно указывать типы, сообщает будущим разработчикам больше смысла о предполагаемом использовании переменной или структуры данных, а также позволяет компилятору обеспечивать лучшую безопасность типов. Вместе эти преимущества могут улучшить качество кода и помочь предотвратить появление некоторых ошибок кодирования.
Например, для метода, в котором программист хочет убедиться, что переменная List с именем 'names' содержит только строки:
List<String> names = new ArrayList<String>();
names.add("John"); // OK
names.add(new Integer(1)); // compile error
Здесь я рассматриваю несколько случаев, с помощью которых вы можете прояснить концепцию
1. ArrayList<String> arr = new ArrayList<String>();
2. ArrayList<String> arr = new ArrayList();
3. ArrayList arr = new ArrayList<String>();
Случай 1
ArrayList<String> arr
это ArrayList
ссылочная переменная с типом String
какая ссылка на ArralyList
Тип объекта String
, Это означает, что он может содержать только объект типа String.
Это строгое String
не Raw Type, поэтому, он никогда не вызовет предупреждение.
arr.add("hello");// alone statement will compile successfully and no warning.
arr.add(23); //prone to compile time error.
//error: no suitable method found for add(int)
Дело 2
В этом случае ArrayList<String> arr
это строгий тип, но ваш объект new ArrayList();
это сырой тип.
arr.add("hello"); //alone this compile but raise the warning.
arr.add(23); //again prone to compile time error.
//error: no suitable method found for add(int)
Вот arr
является строгим типом. Таким образом, это приведет к ошибке времени компиляции при добавлении integer
,
Предупреждение:- А
Raw
Тип Объект ссылается наStrict
Тип Ссылочная переменнаяArrayList
,
Дело 3
В этом случае ArrayList arr
это необработанный тип, но ваш объект new ArrayList<String>();
является строгим типом.
arr.add("hello");
arr.add(23); //compiles fine but raise the warning.
Это добавит любой тип объекта в него, потому что arr
это сырой тип.
Предупреждение:- А
Strict
Тип Объект ссылается наraw
Переменная, на которую ссылается тип
Вот еще один случай, когда необработанные типы будут кусать вас:
public class StrangeClass<T> {
@SuppressWarnings("unchecked")
public <X> X getSomethingElse() {
return (X)"Testing something else!";
}
public static void main(String[] args) {
final StrangeClass<String> withGeneric = new StrangeClass<>();
final StrangeClass withoutGeneric = new StrangeClass();
final String value1,
value2;
// Compiles
value1 = withGeneric.getSomethingElse();
// Produces compile error:
// incompatible types: java.lang.Object cannot be converted to java.lang.String
value2 = withoutGeneric.getSomethingElse();
}
}
Как уже упоминалось в принятом ответе, вы теряете всю поддержку обобщений в коде необработанного типа. Каждый параметр типа преобразуется в его стирание (которое в приведенном выше примере просто Object
).
Компилятор хочет, чтобы вы написали это:
private static List<String> list = new ArrayList<String>();
потому что в противном случае вы можете добавить любой тип, который вам нравится list
, делая экземпляр как new ArrayList<String>()
бессмысленно. Обобщения Java являются только функцией времени компиляции, поэтому объект, созданный с new ArrayList<String>()
с радостью приму Integer
или же JFrame
элементы, если они присвоены ссылке "необработанного типа" List
- сам объект ничего не знает о том, какие типы он должен содержать, делает только компилятор.
Необработанный тип - это отсутствие параметра типа при использовании универсального типа.
Не следует использовать необработанный тип, поскольку это может привести к ошибкам во время выполнения, таким как вставка double
в то, что должно было быть Set
из int
s.
Set set = new HashSet();
set.add(3.45); //ok
При извлечении материала из Set
Вы не знаете, что выходит. Давайте предположим, что вы ожидаете, что это все int
s, вы бросаете его Integer
; исключение во время выполнения, когда double
3.45 приходит вместе.
С параметром типа, добавленным к вашему Set
, вы получите ошибку компиляции сразу. Эта упреждающая ошибка позволяет вам исправить проблему до того, как что-то взорвется во время выполнения (тем самым сэкономив время и усилия).
Set<Integer> set = new HashSet<Integer>();
set.add(3.45); //NOT ok.
Avoid raw types
Raw types refer to using a generic type without specifying a type parameter.
For example,
A list is a raw type, while List<String>
is a parameterized type.
When generics were introduced in JDK 1.5, raw types were retained only to maintain backwards compatibility with older versions of Java. Although using raw types is still possible,
They should be avoided:
- They usually require casts
- They aren't type safe, and some important kinds of errors will only appear at runtime
They are less expressive, and don't self-document in the same way as parameterized typesExample
import java.util.*; public final class AvoidRawTypes { void withRawType() { //Raw List doesn't self-document, //doesn't state explicitly what it can contain List stars = Arrays.asList("Arcturus", "Vega", "Altair"); Iterator iter = stars.iterator(); while (iter.hasNext()) { String star = (String) iter.next(); //cast needed log(star); } } void withParameterizedType() { List < String > stars = Arrays.asList("Spica", "Regulus", "Antares"); for (String star: stars) { log(star); } } private void log(Object message) { System.out.println(Objects.toString(message)); } }
For reference: https://docs.oracle.com/javase/tutorial/java/generics/rawTypes.html
Что говорит то, что ваш list
это List
неуказанных объектов. То есть Java не знает, какие объекты находятся внутри списка. Затем, когда вы хотите выполнить итерацию списка, вы должны привести каждый элемент, чтобы иметь возможность доступа к свойствам этого элемента (в данном случае, String).
В целом, лучше параметризовать коллекции, так что у вас не будет проблем с конвертацией, вы сможете добавлять только элементы параметризованного типа, и ваш редактор предложит вам подходящие методы для выбора.
private static List<String> list = new ArrayList<String>();
Необработанный тип - это имя универсального класса или интерфейса без аргументов типа. Например, учитывая общий класс Box:
public class Box<T> {
public void set(T t) { /* ... */ }
// ...
}
Чтобы создать параметризованный тип Box, вы предоставляете фактический аргумент типа для параметра формального типа T:
Box<Integer> intBox = new Box<>();
Если фактический аргумент типа опущен, вы создаете необработанный тип Box:
Box rawBox = new Box();
Просто для небольшого синтеза: необработанный тип — это универсальный тип без параметра типа (пример: List
это сырой тип List<E>
) Не следует использовать необработанные типы. Они существуют для совместимости со старыми версиями Java. Мы хотим обнаруживать ошибки как можно быстрее (во время компиляции), а использование необработанных типов, вероятно, приведет к ошибкам во время выполнения. Нам по-прежнему нужно использовать необработанные типы в двух случаях:
- Использование литералов класса (List.class)
- Использование instanceof
Примеры :
//Use of raw type : don't !
private final Collection stamps = ...
stamps.add(new Coin(...)); //Erroneous insertion. Does not throw any error
Stamp s = (Stamp) stamps.get(i); // Throws ClassCastException when getting the Coin
//Common usage of instance of
if (o instanceof Set){
Set<?> = (Set<?>) o;
}
Я нашел эту страницу после того, как выполнил несколько типовых упражнений и получил точно такую же головоломку.
============== Я пошел из этого кода, как показано в примере ===============
public static void main(String[] args) throws IOException {
Map wordMap = new HashMap();
if (args.length > 0) {
for (int i = 0; i < args.length; i++) {
countWord(wordMap, args[i]);
}
} else {
getWordFrequency(System.in, wordMap);
}
for (Iterator i = wordMap.entrySet().iterator(); i.hasNext();) {
Map.Entry entry = (Map.Entry) i.next();
System.out.println(entry.getKey() + " :\t" + entry.getValue());
}
====================== К этому коду ========================
public static void main(String[] args) throws IOException {
// replace with TreeMap to get them sorted by name
Map<String, Integer> wordMap = new HashMap<String, Integer>();
if (args.length > 0) {
for (int i = 0; i < args.length; i++) {
countWord(wordMap, args[i]);
}
} else {
getWordFrequency(System.in, wordMap);
}
for (Iterator<Entry<String, Integer>> i = wordMap.entrySet().iterator(); i.hasNext();) {
Entry<String, Integer> entry = i.next();
System.out.println(entry.getKey() + " :\t" + entry.getValue());
}
}
================================================== =============================
Это может быть безопаснее, но понадобилось 4 часа, чтобы разобраться в философии...
Сырые типы хороши, когда они выражают то, что вы хотите выразить.
Например, функция десериализации может возвращать List
, но он не знает тип элемента списка. Так List
подходящий тип возврата здесь.