Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
java all-in-one.docx
Скачиваний:
1
Добавлен:
01.03.2025
Размер:
2 Мб
Скачать
☆

29. Настраиваемые типы данных. Универсальные классы. Ограничения на тип.

По существу настраиваемые типы — это параметризованные типы (parameterized type). Они очень важны, так как позволяют Вам создавать классы,интерфейсы и методы, в которых тип обрабатываемых данных задается как параметр. С помощью настраиваемых типов можно, например, создать один класс, автоматически оперирующий разными типами данных. Класс, интерфейс или метод, работающие с параметризованными типами, называются настраиваемыми (generic). Важно помнить, что в языке Java, всегда существовала возможность создавать обобщенные или универсальные (generalized) классы, интерфейсы и методы с помощью ссылки на тип Object. Поскольку тип Object — это суперкласс для всех остальных классов, ссылка на него, по сути, позволяет ссылаться на объект любого типа. Таким образом, в исходных текстах программ, существовавших до появления настраиваемых типов, обобщенные классы, интерфейсы и методы использовали ссылки на тип Object для работы с различными типами данных. Проблема заключалась в том, что подобная обработка не обеспечивала типовой безопасности. Средства настройки типов обеспечили недостающую типовую безопасность. Они также модернизировали процесс, потому что отпала необходимость в явном приведении типов (Type cast) при переходе от типа Object к конкретному обрабатываемому типу. Благодаря введению настраиваемых типов все приведения выполняются автоматически и скрыто. Таким образом, применение настраиваемых типов расширяет возможности повторного использования кода и делает этот процесс легким и безопасным.

Создадим универсальное описание типа, изменяя код "public class Box" на "public class Box<T>"; здесь вводим одну переменную типа, названную T, которая может использоваться где-нибудь в классе. Эта методика может быть применена также к интерфейсам. Нет ничего особенно сложного в этой концепции. Фактически, это весьма подобно тому, что Вы уже знаете о переменных вообще. Только думайте о T как о специальном виде переменной, "значение" которой будет любым типом, в котором Вы нуждаетесь; это может быть любым типом класса, любым типом интерфейса, или даже другой переменной типа. Она только не может быть ни одинм из примитивных типов данных. В этом контексте мы также говорим, что T - формальный параметр типа класса Box.

Поскольку Вы можете видеть, что мы заменили все вхождения Object с T. Чтобы сослаться на этот универсальный класс изнутри Вашего собственного кода, Вы должны выполнить универсальный вызов типа, который заменяет T некоторым конкретным значением, например, Integer:

Box<Integer> integerBox;

Вы можете думать об универсальном вызове типа как о похожем на обычный вызов метода, но вместо того, чтобы передать аргумент методу, Вы передаете аргумент типа — Integer в этом случае - к классу Box непосредственно. Как любая другая переменная декларация, этот код фактически не создает новый объект Box. Он просто объявляет, что integerBox будет хранить ссылку на "BoxInteger", который является, как Box<Integer> при чтении.

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

Чтобы создать экземпляр этого класса, используйте ключевое слово new, как обычно, но поместите <Integer> между именем класса и круглой скобкой:

integerBox = new Box<Integer>();

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

Box<Integer> integerBox = new Box<Integer>();

Ограничения типа.

Могут быть времена, когда Вы будете хотеть ограничить виды типов, которым позволяют быть переданными параметру типа. Например, метод, который оперирует с числами, мог бы только принять экземпляры класса Number или его подклассов. Это - то, для чего нужны ограниченные параметры типа.

Чтобы объявить ограниченный параметр типа, перечислите имя параметра типа, сопровождаемое ключевым словом extends, сопровождаемым его верхней границей, которая в этом примере является Number. Обратите внимание, что, в этом контексте,extends используется в общем смысле означает, что любой "extends" (как в классах) или "implements" (как в интерфейсах).

/**

* This version introduces a bounded type parameter.

*/

public class Box<T> {

private T t;

public void add(T t) {

this.t = t;

}

public T get() {

return t;

}

public <U extends Number> void inspect(U u){

System.out.println("T: " + t.getClass().getName());

System.out.println("U: " + u.getClass().getName());

}

public static void main(String[] args) {

Box<Integer> integerBox = new Box<Integer>();

integerBox.add(new Integer(10));

integerBox.inspect("some text"); // error: this is still String!

}

}

Изменяя наш универсальный метод, чтобы включить этот ограниченный параметр типа, компиляция будет терпеть неудачу, так как наш вызов inspect все еще включает String:

Box.java:21: <U>inspect(U) in Box<java.lang.Integer> cannot

be applied to (java.lang.String)

integerBox.inspect("10");

^

1 error

Чтобы определить дополнительные интерфейсы, которые должны быть реализованы, используют & символ, как в: 

<U extends Number & MyInterface>

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]