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

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

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

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

package chapt03;

public class Subject <T1, T2> {

private T1 name;

private T2 id;

public Subject() {

}

public Subject(T2 ids, T1 names) {

id = ids;

name = names;

}

}

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

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

Subject<String,Integer> sub =

new Subject<String,Integer>();

char ch[] = {‘J’,’a’,’v’,’a’};

Subject<char[],Double> sub2 =

new Subject<char[],Double>(ch, 71D );

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

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

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

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

класса: Optional.java: Runner.java */

package chapt03;

public class Optional <T> {

private T value;

public Optional() {

}

public Optional(T value) {

this.value = value;

}

public T getValue() {

return value;

}

public void setValue(T val) {

value = val;

}

public String toString() {

if (value == null) return null;

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

}

}

package chapt03;

public class Runner {

public static void main(String[] args) {

//параметризация типом Integer

Optional<Integer> ob1 =

new Optional<Integer>();

ob1.setValue(1);

//ob1.setValue("2");// ошибка компиляции: недопустимый тип

int v1 = ob1.getValue();

System.out.println(v1);

//параметризация типом String

Optional<String> ob2 =

new Optional<String>("Java");

String v2 = ob2.getValue();

System.out.println(v2);

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

//параметризация по умолчанию – Object

Optional ob3 = new Optional();

System.out.println(ob3.getValue());

ob3.setValue("Java SE 6");

System.out.println(ob3.toString());/* выводится

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

ob3.setValue(71);

System.out.println(ob3.toString());

ob3.setValue(null);

}

}

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

1

Java

null

java.lang.String Java SE 6

java.lang.Integer 71

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

Объявление generic-типа в виде <T>, несмотря на возможность использовать любой тип в качестве параметра, ограничивает область применения разрабатываемого класса. Переменные такого типа могут вызывать только методы класса Object. Доступ к другим методам ограничивает компилятор, предупреждая возможные варианты возникновения ошибок.

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

public class OptionalExt <T extends Tип> {

private T value;

}

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

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

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

package chapt03;

public class Mark<T extends Number> {

public T mark;

public Mark(T value) {

mark = value;

}

public T getMark() {

return mark;

}

public int roundMark() {

return Math.round(mark.floatValue());

}

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

public boolean sameAny(Mark<?> ob) {

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

}

public boolean same(Mark<T> ob) {

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

}

}

package chapt03;

public class Runner {

public static void main(String[] args) {

// Mark<String> ms = new Mark<String>(“7”); //ошибка компиляции

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

System.out.println(md.sameAny(md));

Mark<Integer> mi = new Mark<Integer>(71);

System.out.println(md.sameAny(mi));

// md.same(mi); //ошибка компиляции

System.out.println(md.roundMark());

}

}

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

true

true

71

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

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

class Optional <T> {

T value = new T();

}

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

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

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

package chapt03;

class Failed <T1, T2> {

static T1 value;

T2 id;

static T1 getValue() {

return value;

}

static void use() {

System.out.print(id);

}

}

Вы можете следить за любыми ответами на эту запись через RSS 2.0 ленту. Вы можете оставить ответ, или trackback с вашего собственного сайта.

Оставьте отзыв

XHTML: Вы можете использовать следующие теги: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

 
Rambler's Top100