Java raw type parameterized

Java raw type parameterized

Если в среде разработки набрать что-то вроде:
List list = new ArrayList();
то среда выдаст предупреждение, наподобие: «Raw use of parametrized class ‘List'» , что переводится как «Сырое использование параметризованного класса ‘List'». Обычно такой код выделяется средой разработки и может возникнуть вопрос о том, к чему может привести использование так называемых «сырых типов» (raw types).

На самом деле, под сырыми типами подразумеваются не типы данных (классы), а переменные параметризованных (обобщённых) классов, при объявлении которых не указан конкретный тип параметра. Говорят, что они параметризованы сырым, то есть неопределённым, типом.
Сначала приведём пример параметризованного класса:
public class Model K id; >
Мы объявили класс Model , параметризованный K . У него есть поле id , типа K . На данном этапе мы не знаем, какого именно типа будет поле id у объектов этого класса. Действительно, если мы будем хранить в объектах этого класса данные из базы, а в поле id значение первичного ключа, то для разных таблиц тип данных первичного ключа может оказаться разным: где-то это будет Long , а в какой-то таблице — String . Поэтому у нас будут варианты создания переменных типа Model :
Model longId = new Model<>(); Model strId = new Model<>();
Соответственно у переменной longId поле id будет типа Long , а у strId — типа String . Именно то, что нам и нужно. То есть объявляя переменную так: Model longId , мы говорим, что в нашем классе Model переменная типа K равна Long . И во всех местах класса, где встречается K , она будет как бы заменена на Long .
Но, что если мы не укажем тип параметра, объявляя переменную:
Model undefId = new Model<>();
Какого типа будет K в таком случае? Вполне можно считать, что id будет типа Object и следующий код скомпилируется без проблем и отработает без ошибок:
Model undefId = new Model<>(); undefId.id = new Object();
Тем не менее, переменная, объявленная таким образом: Model undefId считается тем самым «сырым использованием параметризованного класса» или «сырым типом» и имеет некоторые отличия от переменной объявленной так: Model objId .

Читайте также:  Вирусы на языке java

Самым частым примером использованием параметризованных классов в Java является использование коллекций. Если переменная, например, списка параметризована, то компилятор будет проверять, что в неё попадают только данные соответствующего типа:
List fruits = new ArrayList<>(); fruits.add(«apple»); fruits.add(«pear»); fruits.add(1);//Ошибка компиляции
Последняя строка примера вообще не скомпилируется, потому что компилятор проверяет, чтобы в метод add передавались только данные типа String , так как мы сами параметризовали список этим типом, объявив его List fruits .
Стоит убрать параметризацию и объявить fruits с «сырым» типом, как проверка прекратится:
List fruits = new ArrayList<>(); fruits.add(«apple»); fruits.add(«pear»); fruits.add(1);//Ошибки не будет
Чтение из такой коллекции всегда сопровождается приведением типа:
String apple = (String) fruits.get(0); String one = (String) fruits.get(2);
Последняя строка выбросит java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String .
Такой код плох тем, что он пройдёт компиляцию, а ошибка обнаружится только во время выполнения. Таким образом, не указывая параметр явно, мы теряем преимущества проверки типов на этапе компиляции.

Рассмотрим пример:
public static void main(String[] args) List strings = new ArrayList<>(); rawAdder(strings); System.out.println(strings.get(0) instanceof String); //false! >
public static void rawAdder(List list) list.add(1); >
Посмотрите внимательно на этот код. Последняя инструкция метода main сделает казалось бы невозможное: она из списка параметризованного типом извлечёт первый элемент и покажет нам, что он не String . Такое возможно из-за крайне неудачной реализации метода rawAdder , а именно из-за того, что его параметр List list не параметризован должным образом.
Если метод rawAdder собирается добавлять в получаемый список случайные объекты, то он и должен принимать список объектов: List list , тогда наша программы просто бы не скомпилировалась и мы бы узнали о проблеме ещё на этапе компиляции, а не во время выполнения. Ошибка была бы в строке rawAdder(strings); , потому что следующее присваивание недопустимо:
List strs = new ArrayList<>(); List objs = new ArrayList<>(); objs = strs;
Такой код не откомпилируется с ошибкой: List cannot be converted to List . А значит и невозможно передать List strings в метод rawAdder(List list) .
Таким образом, переменные, параметризованные типом проходят все проверки компилятора на соответствие типов и исключают ситуации, когда, как в нашем первом примере, в списке явно параметризованном типом всё-таки оказывается элемент типа Integer .

Читайте также:  Python pil размеры изображения

В данном примере:
public static void main(String[] args) List strings = new ArrayList<>(); strings.add(«string»); wildAdder(strings); >
public static void wildAdder(List list) отличается от метода rawAdder(List list) тем, что параметр List параметризован заменителем продолжает действовать проверка типов. указывает на то, что нам неизвестно, какой тип данных возвращает метод get переменной list . А значит компилятор позволит присвоить возвращённое значение только переменной типа Object , которой можно присваивать практически любые значения.
Таким образом, мы можем в этом месте программы передать в метод wildAdder(List list) переменную типа List , а другом месте — переменную типа List . И при этом мы не будем ловить множество ошибок во время выполнения программы, как если бы использовали «сырой тип» List list , без какой-либо параметризации вообще.

В Java, если вы создаёте переменную параметризованного класса, не указывая типы параметров, то есть с «сырыми» типами, то даже заявленные типы параметров конструктора, не статических методов и не статических полей будут затёрты.
Рассмотрим пример:
class Model List getNums() return Arrays.asList(1, 2); > >
Класс Model параметризован . А метод getNames() возвращает список, который тоже параметризован, но не обобщением, а конкретным типом Integer . Так вот. Если создать непараметризованную переменную типа Model , но не указать, какого типа у нас K , то метод getNums() будет возвращать просто List , без параметра. Integer затрётся и компилятор не будет для этого списка проверять типы:
public static void main(String[] args) Model rawType = new Model(); List names = rawType.getNums(); System.out.println(names); >
Мало того, что строка List names = rawType.getNums(); откомпилируется, она ещё и выполнится, выведя на консоль: [1, 2] . А вот следующий код не откомпилируется:
public static void main(String[] args) Model rawType = new Model(); for (Integer num : rawType.getNums()) System.out.print(num); > >
Компилятор выдаст ошибку: error: incompatible types: Object cannot be converted to Integer .
Таким образом, совершенно очевидно, что использование «сырых» типов, то есть создание переменных параметризованных классов без указания конкретных типов — очень плохая практика, чреватая крайне неожиданными побочными эффектами.

Читайте также:  Create file and path java

Обобщения появились лишь в 5-й версии Java, вышедшей в 2004 году. До этого момента не было даже понятия «сырого» типа. Поэтому в целях обратной совместимости, синтаксис языка допускает использование такого стиля, но он крайне не рекомендуем.

  1. При использовании в программе литералов класса: List.class , указать List.class просто не получится.
  2. При использовании оператора instanceof : obj instance of List , а не obj instanceof List . Хотя вариант obj instanceof List тоже возможен, но никаких преимуществ он не даст. В рантайме невозможно выявить чем параметризована переменная, из-за «стирания типов» (type erasure).

Источник

Raw Types

A raw type is the name of a generic class or interface without any type arguments. For example, given the generic Box class:

To create a parameterized type of Box , you supply an actual type argument for the formal type parameter T:

If the actual type argument is omitted, you create a raw type of Box :

Therefore, Box is the raw type of the generic type Box . However, a non-generic class or interface type is not a raw type.

Raw types show up in legacy code because lots of API classes (such as the Collections classes) were not generic prior to JDK 5.0. When using raw types, you essentially get pre-generics behavior — a Box gives you Objects. For backward compatibility, assigning a parameterized type to its raw type is allowed:

Box stringBox = new Box<>(); Box rawBox = stringBox; // OK

But if you assign a raw type to a parameterized type, you get a warning:

Box rawBox = new Box(); // rawBox is a raw type of Box Box intBox = rawBox; // warning: unchecked conversion

You also get a warning if you use a raw type to invoke generic methods defined in the corresponding generic type:

Box stringBox = new Box<>(); Box rawBox = stringBox; rawBox.set(8); // warning: unchecked invocation to set(T)

The warning shows that raw types bypass generic type checks, deferring the catch of unsafe code to runtime. Therefore, you should avoid using raw types.

The Type Erasure section has more information on how the Java compiler uses raw types.

Unchecked Error Messages

As mentioned previously, when mixing legacy code with generic code, you may encounter warning messages similar to the following:

Note: Example.java uses unchecked or unsafe operations. Note: Recompile with -Xlint:unchecked for details.

This can happen when using an older API that operates on raw types, as shown in the following example:

public class WarningDemo < public static void main(String[] args)< Boxbi; bi = createBox(); > static Box createBox() < return new Box(); >>

The term "unchecked" means that the compiler does not have enough type information to perform all type checks necessary to ensure type safety. The "unchecked" warning is disabled, by default, though the compiler gives a hint. To see all "unchecked" warnings, recompile with -Xlint:unchecked.

Recompiling the previous example with -Xlint:unchecked reveals the following additional information:

WarningDemo.java:4: warning: [unchecked] unchecked conversion found : Box required: Box bi = createBox(); ^ 1 warning

To completely disable unchecked warnings, use the -Xlint:-unchecked flag. The @SuppressWarnings("unchecked") annotation suppresses unchecked warnings. If you are unfamiliar with the @SuppressWarnings syntax, see Annotations.

Источник

Оцените статью