Что нельзя параметризовать java

Обобщения (Generic)

Рассмотрим пример с обобщением.

В результате мы получим:

Изучим код. Мы объявили класс в следующей форме:

Далее тип T используется для объявления объекта по имени ob:

Вместо T подставится реальный тип, который будет указан при создании объекта класса Gen. Объект ob будет объектом типа, переданного в параметре типа T. Если в параметре T передать тип String, то экземпляр ob будет иметь тип String.

Рассмотрим конструктор Gen().

Параметр o имеет тип T. Это значит, что реальный тип параметра o определяется типом, переданным параметром типа T при создании объекта класса Gen.

Параметр типа T также может быть использован для указания типа возвращаемого значения метода:

Как использовать обобщённый класс. Можно создать версию класса Gen для целых чисел:

В угловых скобках указан тип Integer, т.е. это аргумент типа, который передаётся в параметре типа T класса Gen. Фактически мы создаём версию класса Gen, в которой все ссылки на тип T становятся ссылками на тип Integer.

Когда мы присваиваем ссылку на экземпляр, то угловые скобки также требуется указывать.

Полная версия записи может быть такой:

Но такая запись избыточна, так как можно использовать автоматическую упаковку значения 77 в нужный формат.

Аналогично, можно было бы использовать вариант без автоупаковки для получения значения:

Обобщения работают только с объектами. Поэтому нельзя использовать в качестве параметра элементарные типы вроде int или char:

Использование обобщений автоматически гарантирует безопасность типов во всех операциях, где они задействованы. Это очень мощный механизм, широко используемый в Java.

Обобщённый класс с двумя параметрами

Можно указать два и более параметров типа через запятую.

Обобщённые методы

Никто не запрещает создавать и методы с параметрами и возвращаемыми значениями в виде обобщений.

Шаблоны аргументов

Шаблон аргументов указывается символом ? и представляет собой неизвестный тип.

По сути, вопрос заменяет Object и мы можем использовать любой класс, который в любом случае будет происходить от Object.

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

Пример обобщённого метода

Переменная типа вводится после модификаторов и перед возвращаемым типом.

Отдельно стоит упомянуть новинку JDK 7, позволяющую сократить код.

Во второй строке используются только угловые скобки, без указания типов.

Помните, что нельзя создать экземпляр типа параметра.

Под Android вам часто придётся использовать списочный массив, который использует обобщещние.

В Java 7, которую можно использовать в проектах для KitKat и выше, существует укороченная запись, когда справа не указывается тип (см. выше). Компилятор сам догадается по левой части выражения.

Источник

Параметризованные классы Java

К наиболее важным новшествам версии языка J2SE 5 можно отнести появление параметризованных (generic) классов и методов, позволяющих использовать более гибкую и в то же время достаточно строгую типизацию, что особенно важно при работе с коллекциями. Применение generic-классов для создания типизированных коллекций будет рассмотрено в главе «Коллекции». Параметризация позволяет создавать классы, интерфейсы и методы, в которых тип обрабатываемых данных задается как параметр.

Приведем пример generic-класса с двумя параметрами:

/*пример # 9 : объявление класса с двумя параметрами : Subject.java */

public class Subject <

public Subject(T2 ids, T1 names) <

Здесь T1, Т2 – фиктивные объектные типы, которые используются при объявлении членов класса и обрабатываемых данных. При создании объекта компилятор заменит все фиктивные типы на реальные и создаст соответствующий им объект. В качестве параметров классов запрещено применять базовые типы.

Объект класса Subject можно создать, например, следующим образом:

new Subject (ch, 71D );

В объявлении sub2 имеет место автоупаковка значения 71D в Double.

Параметризированные типы обеспечивают типовую безопасность.

Ниже приведен пример параметризованного класса Optional с конструкторами и методами, также инициализация и исследование поведения объектов при задании различных параметров.

/*пример # 10 : создание и использование объектов параметризованного

public class Optional <

public Optional(T value) <

public void setValue(T val) <

public String toString() <

return value.getClass().getName() + » » + value;

public static void main(String[] args) <

int v1 = ob1.getValue();

String v2 = ob2.getValue();

//ob1 = ob2; //ошибка компиляции – параметризация не ковариантна

Optional ob3 = new Optional();

ob3.setValue(«Java SE 6»);

тип объекта, а не тип параметризации */

В результате выполнения этой программы будет выведено:

java.lang.String Java SE 6

В рассмотренном примере были созданы объекты типа Optional: ob1 на основе типа Integer и ob2 на основе типа String при помощи различных конструкторов. При компиляции вся информация о generic-типах стирается и заменяется для членов класса и методов заданными типами или типом Object, если параметр не задан, как для объекта ob3. Такая реализация необходима для обеспечения совместимости с кодом, созданным в предыдущих версиях языка.

Чтобы расширить возможности параметризованных членов класса, можно ввести ограничения на используемые типы при помощи следующего объявления класса:

public class OptionalExt <

Такая запись говорит о том, что в качестве типа Т разрешено применять только классы, являющиеся наследниками (суперклассами) класса Tип, и соответственно появляется возможность вызова методов ограничивающих (bound) типов.

Часто возникает необходимость в метод параметризованного класса одного допустимого типа передать объект этого же класса, но параметризованного другим типом. В этом случае при определении метода следует применить метасимвол ?. Метасимвол также может использоваться с ограничением extends для передаваемого типа.

/*пример # 11 : использование метасимвола в параметризованном классе: Mark.java, Runner.java */

public Mark(T value) <

/* вместо */ // public boolean sameAny(Mark ob) <

public boolean sameAny(Mark ob) <

return roundMark() == ob.roundMark();

public boolean same(Mark ob) <

return getMark() == ob.getMark();

public static void main(String[] args) <

Mark md = new Mark (71.4D);//71.5d

Mark mi = new Mark (71);

В результате будет выведено:

Метод sameAny(Mark ob) может принимать объекты типа Mark, инициализированные любым из допустимых для этого класса типов, в то время как метод с параметром Mark мог бы принимать объекты с инициализацией того же типа, что и вызывающий метод объект.

Для generic-типов существует целый ряд ограничений. Например, невозмож­но выполнить явный вызов конструктора generic-типа:

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

По аналогичным причинам generic-поля не могут быть статическими, статические методы не могут иметь generic-параметры или обращаться к generic-полям, например:

/*пример # 12 : неправильное объявление полей параметризованного класса: Failed.java */

Источник

Core Java

Курс лекций. Лекция 6

Иван Пономарёв, КУРС/МФТИ

До появления дженериков

Что нельзя параметризовать java

После появления дженериков

Определяем собственный параметризованный класс

Определение и использование

Generic methods

Другой пример

Промежуточные выводы

Использование параметризованных классов — простое (просто укажите параметры, List )

Использование параметризованных методов — ещё проще: type inference: Manager m = getRandomItem(…​);

Написание собственных параметризованных классов и методов — задачка более сложная.

Bounded types

Intersection types

Что нельзя параметризовать java

Реализация дженериков

Появились в Java 5

Стояла задача обратной совместимости

Generics — возможность языка, а не платформы

Type Erasure, будь он неладен!

Сырые типы

Generic Type (source)

Raw Type (compiled)

Ограниченные типы вместо Object

Generic Type (source)

Raw Type (compiled)

Вызовы методов

Source code

Compiled

Bridge methods для сохранения полиморфизма

Source code

Compiled

Итог: как это работает

Параметризованных классов в JVM нет, только обычные классы и методы.

Типовые параметры заменяются на Object или на свою границу.

Для сохранения полиморфизма добавляются связывающие методы (bridge methods).

Сведение типов добавляется по мере необходимости.

Никогда не употребляйте сырые типы

Возможность назначать переменным сырые типы оставлена для обратной совместимости с кодом, написанным до Java5.

Java5 вышла в 2004 году.

Понимание дженериков в Джаве — это не про то, что с ними делать можно, а про то, что с ними делать нельзя.

Стирание типов → невозможность определить параметр типа в Runtime

Что нельзя параметризовать java

Стирание типов до Object → невозможность использовать примитивные типы в качестве параметров

Примитивы и дженерики

День сегодняшний: нужна производительность? — пишем специальные реализации.

В стандартной библиотеке:

В специализированных библиотеках вроде fastutil:

HashMap → Int2ObjectMap (ВНИМАНИЕ: реальная потребность в таких библиотеках возникает редко!!)

День завтрашний: Project Valhalla, specialized generics. Решит проблему раз и навсегда.

Нельзя инстанцировать типы-параметры

Решается с помощью метакласса и рефлексии (о которой речь впереди)

Тем более нельзя инстанцировать массив типа-параметра

Решается передачей параметра, например, в ArrayList:

Массивы и дженерики — лютые враги

Забьём значения кувалдой и устроим heap pollution

Varargs — всё равно массив…​

Тот же heap pollution, что и в прошлом примере:

Компилятор что-то предчувствует…​

Что нельзя параметризовать java

Чтобы успокоить компилятор, надо поставить аннотацию @SafeVarargs :

…​и компилятор успокоится.

Зачем?!

Всё потому, что иметь varargs с параметризованными типами удобно.

Collections.addAll(Collection c, T…​ elements)

EnumSet.of(E first, E…​ rest)

Если вести себя хорошо, можно ставить @SafeVarargs, и всё будет хорошо:

Не записывать ничего в элементы массива,

Не раздавать ссылку на массив параметров наружу.

Нельзя параметризовать

ловля исключений — это проверка их типов,

дальше сырых типов мы в рантайме проверить не можем.

Инстанцируется по месту, не может быть несколько классов, параметризованных по-разному.

Параметры типов нельзя использовать в статическом контексте

Нельзя реализовывать разные параметризации одного и того же интерфейса

Source code

Compiled

Ковариантность массивов vs инвариантность дженериков

Что нельзя параметризовать java

Что нельзя параметризовать java

Что нельзя параметризовать java

Реальная картина

Что нельзя параметризовать java

Как быть, если хочется такого?

Что нельзя параметризовать java

Так не получится…​

Wildcard Types

Что нельзя параметризовать java

В общем, addAllTo реализовать не получится…​

В обратную сторону (контравариантные типы)

Что нельзя параметризовать java

Unbounded wildcard

Класть можем только null.

Мнемоническое правило

PECS

Producer Extends, Consumer Super

Правила использования wildcard-типов

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

Невидимы пользователю API, не должны возвращаться.

Их цель — принимать те аргументы, которые надо принимать, и отвергать те аргументы, которые надо отвергать.

Должны быть используемы в API, иначе API будет слишком «жёстким» и непригодным для использования.

Wildcard Capture

Метод с type capture

Recursive Generics

Что почитать-посмотреть

J. Bloch, Effective Java, 3rd ed. Chapter 5 — Generics. Addison-Wesley, 2018

Источник

Пришел, увидел, обобщил: погружаемся в Java Generics

Java Generics — это одно из самых значительных изменений за всю историю языка Java. «Дженерики», доступные с Java 5, сделали использование Java Collection Framework проще, удобнее и безопаснее. Ошибки, связанные с некорректным использованием типов, теперь обнаруживаются на этапе компиляции. Да и сам язык Java стал еще безопаснее. Несмотря на кажущуюся простоту обобщенных типов, многие разработчики сталкиваются с трудностями при их использовании. В этом посте я расскажу об особенностях работы с Java Generics, чтобы этих трудностей у вас было поменьше. Пригодится, если вы не гуру в дженериках, и поможет избежать много трудностей при погружении в тему.

Что нельзя параметризовать java

Работа с коллекциями

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

С появлением Generics необходимость в проверке и приведении типа отпала:

Во второй строчке проверки необходимость тоже отпадала. Если потребуется, приведение типов ( casting ) будет сделано на этапе компиляции.

Принцип подстановки

ТипПодтип
NumberInteger
ListArrayList
CollectionList
IterableCollection

Примеры отношения тип/подтип

Вот пример использования принципа подстановки в Java:

Ковариантность, контравариантность и инвариантность

Но если мы попытаемся изменить содержимое массива через переменную arr и запишем туда число 42, то получим ArrayStoreException на этапе выполнения программы, поскольку 42 является не строкой, а числом. В этом недостаток ковариантности массивов Java: мы не можем выполнить проверки на этапе компиляции, и что-то может сломаться уже в рантайме.

«Дженерики» инвариантны. Приведем пример:

Wildcards

Всегда ли Generics инварианты? Нет. Приведу примеры:

Это ковариантность. List — подтип List

Что нельзя параметризовать javaextends B — символ подстановки с указанием верхней границы
super B — символ подстановки с указанием нижней границы
где B — представляет собой границу

2. Почему нельзя получить элемент из списка ниже?

The Get and Put Principle или PECS (Producer Extends Consumer Super)

Особенность wildcard с верхней и нижней границей дает дополнительные фичи, связанные с безопасным использованием типов. Из одного типа переменных можно только читать, в другой — только вписывать (исключением является возможность записать null для extends и прочитать Object для super ). Чтобы было легче запомнить, когда какой wildcard использовать, существует принцип PECS — Producer Extends Consumer Super.

и Raw типы

Если мы опустим указание типа, например, как здесь:

Если мы попытаемся вызвать параметризованный метода у Raw типа, то компилятор выдаст нам предупреждение «Unchecked call». Если мы попытаемся выполнить присваивание ссылки на параметризованный тип Raw типу, то компилятор выдаст предупреждение «Unchecked assignment». Игнорирование этих предупреждений, как мы увидим позже, может привести к ошибкам во время выполнения нашего приложения.

Wildcard Capture

Попробуем теперь реализовать метод, выполняющий перестановку элементов списка в обратном порядке.

Более подробно о Wildcard Capture можно прочитать здесь и здесь.

Вывод

Переменные типа

Вот еще пример из класса Enum:

Multiple bounds (множественные ограничения)

Вывод

Переменная типа может быть ограничена только сверху одним или несколькими типами. В случае множественного ограничения левая граница (первое ограничение) используется в процессе затирания (Type Erasure).

Type Erasure

На скриншоте ниже два примера программы:
Что нельзя параметризовать java

Разница между ними в том, что слева происходит compile-time error, а справа все компилируется без ошибок. Почему?

Reifiable типы

Почему информация об одних типах доступна, а о других нет? Дело в том, что из-за процесса затирания типов компилятором информация о некоторых типах может быть потеряна. Если она потерялась, то такой тип будет уже не reifiable. То есть она во время выполнения недоступна. Если доступна – соответственно, reifiable.

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

И еще одна задачка. Почему в примере ниже нельзя создать параметризованный Exception?

Каждое catch выражение в try-catch проверяет тип полученного исключения во время выполнения программы (что равносильно instanceof), соответственно, тип должен быть Reifiable. Поэтому Throwable и его подтипы не могут быть параметризованы.

Unchecked Warnings

Компиляция нашего приложения может выдать так называемый Unchecked Warning — предупреждение о том, что компилятор не смог корректно определить уровень безопасности использования наших типов. Это не ошибка, а предупреждение, так что его можно пропустить. Но желательно все-так исправить, чтобы избежать проблем в будущем.

Heap Pollution

Как мы упомянули ранее, присваивание ссылки на Raw тип переменной параметризованного типа, приводит к предупреждению «Unchecked assignment». Если мы проигнорируем его, то возможна ситуация под названием » Heap Pollution » (загрязнение кучи). Вот пример:

В строке (1) компилятор предупреждает об «Unchecked assignment».

Рассмотрим еще один пример:

Java разрешает выполнить присваивание в строке (1). Это необходимо для обеспечения обратной совместимости. Но если мы попытаемся выполнить метод add в строке (2), то получим предупреждение Unchecked call — компилятор предупреждает нас о возможной ошибке. В самом деле, мы же пытаемся в список строк добавить целое число.

Reflection

Хотя при компиляции параметризованные типы подвергаются процедуре стирания (type erasure), кое-какую информацию мы можем получить с помощью Reflection.

С появлением Generics класс java.lang.Class стал параметризованным. Рассмотрим вот этот код:

Вывод

Если информация о типе доступна во время выполнения программы, то такой тип называется Reifiable. К Reifiable типам относятся: примитивные типы, непараметризованные типы, параметризованные типы с неограниченным символом подстановки, Raw типы и массивы, элементы которых являются reifiable.

Игнорирование Unchecked Warnings может привести к «загрязнению кучи» и ошибкам во время выполнения программы.

Reflection не позволяет получить информацию о типе объекта, если он не Reifiable. Но Reflection позволяет получить информацию о типе возвращаемого методом значения, о типе аргументов метода и о типе полей класса.

Type Inference

Термин можно перевести как «Вывод типа». Это возможность компилятора определять (выводить) тип из контекста. Вот пример кода:

С появлением даймонд-оператора в Java 7 мы можем не указывать тип у ArrayList :

Предположим у нас есть вот такой класс, который описывает связный список:

Результат обобщенного метода List.nil() может быть выведен из правой части:

Механизм выбора типа компилятором показывает, что аргумент типа для вызова List.nil() действительно String — это работает в JDK 7, все хорошо.

Выглядит разумно, что компилятор также должен иметь возможность вывести тип, когда результат такого вызова обобщенного метода передается другому методу в качестве аргумента, например:

В JDK 7 мы получили бы compile-time error. А в JDK 8 скомпилируется. Это и есть первая часть JEP-101, его первая цель — вывод типа в позиции аргумента. Единственная возможность осуществить это в версиях до JDK 8 — использовать явный аргумент типа при вызове обобщенного метода:

Вторая часть JEP-101 говорит о том, что неплохо бы выводить тип в цепочке вызовов обобщенных методов, например:

Но данная задача не решена до сих пор, и вряд ли в ближайшее время появится такая функция. Возможно, в будущих версиях JDK необходимость в этом исчезнет, но пока нужно указывать аргументы вручную:

После выхода JEP 101 на StackOverflow появилось множество вопросов по теме. Программисты спрашивают, почему код, который выполнялся на 7-й версии, на 8-й выполняется иначе – или вообще не компилируется? Вот пример такого кода:

Посмотрим на байт-код после компиляции на JDK1.8:

А теперь байт-код после компиляции на JDK1.7 – то есть на Java 7:

Чтобы избежать таких проблем, Oracle выпустил руководство по переходу с JDK1.7 на JDK 1.8 в котором описаны проблемы, которые могут возникнуть при переходе на новую версию Java, и то, как эти проблемы можно решить.

Например если вы хотите, чтобы в коде выше после компиляции на Java 8 все работало так же, как и на Java 7, сделайте приведение типа вручную:

Заключение

На этом мой рассказ о Java Generics подходит к концу. Вот другие источники, которые помогут вам в освоении темы:

Источник

Что нельзя параметризовать java

Параметризованные классы Java

К наиболее важным новшествам версии языка J2SE 5 можно отнести появление параметризованных (generic) классов и методов, позволяющих использовать более гибкую и в то же время достаточно строгую типизацию, что особенно важно при работе с коллекциями. Применение generic-классов для создания типизированных коллекций будет рассмотрено в главе «Коллекции». Параметризация позволяет создавать классы, интерфейсы и методы, в которых тип обрабатываемых данных задается как параметр.

Приведем пример generic-класса с двумя параметрами:

/*пример # 9 : объявление класса с двумя параметрами : Subject.java */

public class Subject sub =

new Subject (ch, 71D );

В объявлении sub2 имеет место автоупаковка значения 71D в Double.

Параметризированные типы обеспечивают типовую безопасность.

Ниже приведен пример параметризованного класса Optional с конструкторами и методами, также инициализация и исследование поведения объектов при задании различных параметров.

/*пример # 10 : создание и использование объектов параметризованного

int v1 = ob1.getValue();

String v2 = ob2.getValue();

//ob1 = ob2; //ошибка компиляции – параметризация не ковариантна

Optional ob3 = new Optional();

тип объекта, а не тип параметризации */

В результате выполнения этой программы будет выведено:

java.lang.String Java SE 6

В рассмотренном примере были созданы объекты типа Optional: ob1 на основе типа Integer и ob2 на основе типа String при помощи различных конструкторов. При компиляции вся информация о generic-типах стирается и заменяется для членов класса и методов заданными типами или типом Object, если параметр не задан, как для объекта ob3. Такая реализация необходима для обеспечения совместимости с кодом, созданным в предыдущих версиях языка.

Чтобы расширить возможности параметризованных членов класса, можно ввести ограничения на используемые типы при помощи следующего объявления класса:

public class OptionalExt ob) ob) ob) ms = new Mark (“7”); //ошибка компиляции

Mark md = new Mark (71.4D);//71.5d

Mark mi = new Mark (71);

В результате будет выведено:

Метод sameAny(Mark ob) может принимать объекты типа Mark, инициализированные любым из допустимых для этого класса типов, в то время как метод с параметром Mark мог бы принимать объекты с инициализацией того же типа, что и вызывающий метод объект.

Для generic-типов существует целый ряд ограничений. Например, невозмож­но выполнить явный вызов конструктора generic-типа:

class Optional integerMatrix Integer является аргументом типа.

Дженерики работают только с объектами! Следующий код является неправильным:

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

Обобщенные типы отличаются в зависимости от типов-аргументов. Следующий код не допустим:

Обобщенный класс может быть объявлен с любым количеством параметров типа. Например:

2. Ограниченные типы

Указывая параметр типа, можно наложить ограничение сверху в виде верхней границы, где объявляется супер класс, от которого должны быть унаследованы все аргументы типов. С этой целью вместе с параметром указывается ключевое слово extends :

Параметр типа Т может быть заменен только указанным супер классом или его подклассами.

Рассмотрим пример использования ограниченного типа:

В виде ограничения можно накладывать не только тип класса, но и тип интерфейса:

Ограничение может включать в себя как тип класса, так и типы одного или нескольких интерфейсов:

3. Применение метасимвольных аргументов

Представьте, что мы хотим добавить метод для сравнения средних значений массивов в класс Average из примера 3. Причем типы массивов могут быть разные:

Но это не сработает, так как в этом случае метод sameAvg будет принимать аргументы только того же типа, что и существующий объект:

Метасимвольные аргументы могут быть ограничены почти таким же образом, как и параметры типов. Ограничивать метасимвольный аргумент особенно важно при создании обобщенного типа, оперирующего иерархией классов. Например:

4. Параметризованные методы и конструкторы

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

Но можно объявить параметризованный метод, в котором непосредственно используется один или несколько параметров типа. Более того, можно объявить параметризованный метод, входящий в не параметризованный класс. Например:

Конструкторы также могут быть обобщенными, даже если их классы таковыми не являются. Например:

5. Параметризованные интерфейсы

В дополнение к обобщенным классам и методам вы можете объявлять параметризованные интерфейсы. Параметризованные интерфейсы специфицируются так же, как и обобщенные классы:

6. Иерархии параметризованных классов

Параметризованные классы могут быть частью иерархии классов так же, как и любые другие не параметризованные классы. То есть параметризованный класс может выступать в качестве супер класса или подкласса.

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

Подкласс параметризованного супер класса необязательно должен быть параметризованным, но в нем все же должны быть, указаны параметры типа, требующиеся его параметризованному супер классу. Подкласс может, если требуется, быть, дополнен и своими параметрами типа. Супер классом для параметризованного класса может быть класс не параметризованный.

7. Использование оператора instanceof c параметризованными классами

8. Ограничения присущие обобщениям

Обобщениям присущи некоторые ограничения. Рассмотрим их:

1. Нельзя создавать экземпляр по параметру типа. Ни обычный объект, ни массив:

2. Нельзя создать массив специфических для типа обобщенных ссылок:

3. Нельзя создавать обобщенные статические переменные и методы. Но объявить статические обобщенные методы со своими параметрами типа все же можно:

Вопрос по дженерикам метода и ограничению переменных типов

Сейчас активно изучаю (или даже разбираю) известное пособие Хорстмена и Корнелла по Java2. В данный момент остановился на параметризации. Дошел до пункта «Обобщенные методы» и следующий за ним «Ограничения переменных типов». И в ближайшем листинге среди прочего кода появляется такая сигнатура:

Я понимаю, что в методе применяется метод compareTo(), принадлежащий интерфейсу Comparable и поэтому мы должны «защитить» наш метод от того, чтобы туда не затесался класс, который не реализует данный интерфейс. Ок, понятно. Но почему бы тогда нельзя было написать:

С другой стороны, возможно имелось в виду, что это не объект класса Pair должен возвращать метод, а объект любого класса, реализующий Comparable? Тогда почему не написать так:

Я не отрицаю, что в пособии вполне может быть указана правильная сигнатура. И если это так, то прошу подробно описать ее. Заранее спасибо за развернутые ответы!

Вы хотите развёрнутых ответов? Их есть у меня! Немного теории по параметризованным методам.

Если метод нестатический, а класс — параметризованный, то параметр типа наследуется из описания класса:

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

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

Ещё один случай, когда может понадобиться явно параметризовать метод — это нестатический метод, параметр типа которого должен отличаться от параметра типа класса:

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

Теперь непосредственно к вашему вопросу. Нельзя просто написать

Почему нельзя написать

Источник

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *