Spec-Zone .ru
спецификации, руководства, описания, API
|
Универсальным типом является универсальный class, или взаимодействуйте через интерфейс, который параметризован по типам. Следующий Box class будет изменен, чтобы демонстрировать понятие.
Начните, исследуя неуниверсальный Box class, который работает на объектах любого типа. Это должно только обеспечить два метода: set, который добавляет объект к полю, и get, который получает это:
public class Box { private Object object; public void set(Object object) { this.object = object; } public Object get() { return object; } }
Так как его методы принимают или возвращают Object, Вы свободны передать, в чем Вы хотите, при условии, что это не одни из типов примитивов. Нет никакого способа проверить, во время компиляции, как class используется. Одна часть кода может поместить Integer в поле и ожидать получать Integer s из этого, в то время как другая часть кода может по ошибке передать в String, приводящем к ошибке периода выполнения.
Универсальный class определяется с помощью следующего формата:
class name<T1, T2, ..., Tn> { /* ... */ }
Раздел параметра типа, разграниченный угловыми скобками (<>), следует за именем class. Это определяет параметры типа (также названный переменными типа) T1, T2..., и Tn.
Чтобы обновить Box class, чтобы использовать обобщения, Вы создаете универсальное описание типа, изменяя код "public class Box" на "public class Box<T>". Это представляет переменную типа, T, который может использоваться где угодно в class.
С этим изменением Box class становится:
/** * Generic version of the Box class. * @param <T> the type of the value being boxed */ public class Box<T> { // T stands for "Type" private T t; public void set(T t) { this.t = t; } public T get() { return t; } }
Как можно видеть, все возникновения Object заменяются T. Переменная типа может быть любым нетипом примитива, который Вы определяете: любой тип class, любой тип интерфейса, любой тип массива, или даже другая переменная типа.
Этот тот же самый метод может быть применен, чтобы создать универсальные интерфейсы.
Условно, введите названия параметра, единственные, прописные буквы. Это стоит в резком контрасте переменным соглашениям о присвоении имен, которые Вы уже знаете о, и с серьезным основанием: Без этого соглашения было бы трудно сказать различие между переменной типа и обычным class или именем интерфейса.
Обычно используемые названия параметра типа:
Вы будете видеть эти имена, используемые всюду по Java API SE и остальная часть этого урока.
Чтобы сослаться на универсальный Box class изнутри Вашего кода, следует выполнить универсальный вызов типа, который заменяет T некоторым конкретным значением, таким как Integer:
Box<Integer> integerBox;
Можно думать об универсальном вызове типа, как являющемся подобным обычному вызову метода, но вместо того, чтобы передать параметр методу, Вы передаете параметр типа — Integer в этом случае — к Box class непосредственно.
Как любое другое объявление переменной, этот код фактически не создает новый объект Box. Это просто объявляет, что integerBox будет содержать ссылку на "Box Integer", который является, как Box<Integer> читается.
Вызов универсального типа является общеизвестным как параметризованный тип.
Чтобы инстанцировать этого class, используйте ключевое слово new, как обычно, но поместите <Integer> между именем class и круглой скобкой:
Box<Integer> integerBox = new Box<Integer>();
В Java SE 7 и позже, можно заменить параметры типа, требуемые вызвать конструктора универсального class с пустым множеством параметров типа (<>), пока компилятор может определить, или вывести, параметры типа от контекста. Эту пару угловых скобок, <>, неофициально вызывают ромбом. Например, можно создать экземпляр Box<Integer> со следующим оператором:
Box<Integer> integerBox = new Box<>();
Для получения дополнительной информации по ромбовидной нотации и выводу типа, см. Вывод типа.
Как упомянуто ранее, у универсального class могут быть многократные параметры типа. Например, универсальный OrderedPair class, который реализует универсальный интерфейс Pair:
public interface Pair<K, V> { public K getKey(); public V getValue(); } public class OrderedPair<K, V> implements Pair<K, V> { private K key; private V value; public OrderedPair(K key, V value) { this.key = key; this.value = value; } public K getKey() { return key; } public V getValue() { return value; } }
Следующие операторы создают два инстанцирования OrderedPair class:
Pair<String, Integer> p1 = new OrderedPair<String, Integer>("Even", 8); Pair<String, String> p2 = new OrderedPair<String, String>("hello", "world");
Код, new OrderedPair<String, Integer>, инстанцирует K как String и V как Integer. Поэтому, типами параметра конструктора OrderedPair является String и Integer, соответственно. Из-за автоупаковки, это допустимый, чтобы передать String и int к class.
Как упомянуто в Ромбе, потому что компилятор Java может вывести K и типы V от объявления OrderedPair<String, Integer>, эти операторы могут быть сокращены, используя ромбовидную нотацию:
OrderedPair<String, Integer> p1 = new OrderedPair<>("Even", 8); OrderedPair<String, String> p2 = new OrderedPair<>("hello", "world");
Чтобы создать универсальный интерфейс, следуйте за теми же самыми соглашениями что касается создания универсального class.
Можно также заменить параметром типа (то есть, K или V) с параметризованным типом (то есть, List<String>). Например, используя пример OrderedPair<V, K>:
OrderedPair<String, Box<Integer>> p = new OrderedPair<>("primes", new Box<Integer>(...));