Философия Java3
Шрифт:
}
public static void main(String[] args) { GenericArray2<Integer> gai =
new GenericArray2<Integer>(10); for(int i = 0: i < 10: i ++)
gai.put(i, i): for(int i = 0: i < 10; i ++)
System.out.print(gai.get(i) + " "); System.out.printlnO; try {
Integer[] ia = gai.rep; } catch(Exception e) { System.out.printin(e); }
}
} /* Output: (Sample)
0 12 3 4 5 6 7 8 9
java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to [Ljava.lang.Integer,
На первый взгляд почти ничего не изменилось, разве что преобразование типа было перемещено.
В новом коде следует передавать метку типа. В обновленной версии Generic-Array выглядит так:
//: generics/Generic/\rrayWithTypeToken.java
import java.lang.reflect.*;
public class GenericArrayWithTypeToken<T> { private T[] array; @SuppressWarni ngs("unchecked")
public GenericArrayWithTypeToken(CIass<T> type, int sz) { array = (T[])Array.newInstance(type, sz);
}
public void put(int index, T item) { arrayCindex] = item;
}
public T get(int index) { return arrayCindex]; } // Expose the underlying representation: public T[] rep { return array; } public static void main(String[] args) {
GenericArrayWithTypeToken<Integer> gai =
new Generi cArrayWi thTypeToken<Integer>( Integer.class, 10);
// This now works:
Integer[] ia = gai.rep;
}
} ///
Метка типа Class<T> передается конструктору для восстановления информации после стирания, чтобы мы могли создать фактический тип нужного массива (предупреждения при преобразовании по-прежнему приходится подавлять @SuppressWarnings). Получив фактический тип, мы возвращаем его для получения желаемых результатов, как видно из main.
К сожалению, просмотрев исходный код стандартных библиотек Java SE5, вы увидите, что преобразования массивов Object в параметризованные типы происходят повсеместно. Например, вот как выглядит копирующий конструктор для создания ArrayList из Collection после некоторой правки и упрощения:
public ArrayList(Collection с) { size = c.sizeO;
elementData = (E[])new Object[size]; с.toArray(elementData):
}
В ArrayList.java подобные преобразования встречаются неоднократно. И конечно, при их компиляции выдается множество предупреждений.
Ограничения
Ограничения, уже упоминавшиеся ранее в этой главе, сужают круг параметров типов, используемых при параметризации. Хотя это позволяет предъявлять требования к типам, к которым применяется ваш параметризованный код, у ограничений имеется и другой, потенциально более важный эффект: возможность вызова методов, определенных
в ограничивающих типах.Поскольку стирание уничтожает информацию о типе, при отсутствии ограничений для параметров типов могут вызываться только методы Object. Но, если ограничить параметр подмножеством типов, вы сможете вызвать методы из этого подмножества. Для установления ограничений в Java используется ключевое слово extends. Важно понимать, что в контексте параметризации extends имеет совершенно иной смысл, нежели в обычной ситуации. Следующий пример демонстрирует основы установления ограничений:
//: generics/BasicBounds.java
interface HasColor { java. awt. Col or getColorO; }
class Colored<T extends HasColor> { T item:
Colored(T item) { this.item = item; }
T getltemO { return item; }
// Ограничение позволяет вызвать метод:
java. awt. Col or colore) { return item.getColorO; }
}
class Dimension { public int x, y. z; }
// Не работает -- сначала класс, потом интерфейсы: // class ColoredDimensiол<Т extends HasColor & Dimension> {
// Несколько ограничений-
class ColoredDimension<T extends Dimension & HasColor> { T item:
ColoredDimension(T item) { this.item = item, }
T getltemO { return item, }
java. awt. Col or colorO { return item getColorO; }
int getXO { return item.x; }
int getYO { return item.у, }
int getZO { return item z; }
}
interface Weight { int weightO; }
// Как и при наследовании, конкретный класс может быть только один, // а интерфейсов может быть несколько: class Solid<T extends Dimension & HasColor & Weight> { T item;
Solid(T item) { this.item = item. }
T get ItemО { return item; }
java.awt Color col orО { return item.getColor; }
int getXO { return item x; }
int getYO { return item у; }
int getZO { return item.z; }
int weightO { return item, weight О; }
}
class Bounded
extends Dimension implements HasColor. Weight {
public java.awt.Col or getColorO { return null; } public int weightO { return 0; }
}
public class BasicBounds {
public static void main(String[] args) { Solid<Bounded> solid =
new Solid<Bounded>(new BoundedO); solid.colorO; solid.getYO. solid.weightO;
}
} ///
Вероятно, вы заметили, что пример BasicBounds.java содержат некоторую избыточность, которая может быть устранена посредством наследования. С каждым уровнем наследования добавляются новые ограничения:
II: generics/InheritBounds.java
class HoldItem<T> { T item;
HoldItem(T item) { this.item = item; } T getltemO { return item; }
}
class Colored2<T extends HasColor> extends HoldItem<T> {
Colored2(T item) { super(item). }
java awt Color col or О { return item.getColorO; }
}
class ColoredDimension2<T extends Dimension & HasColor> extends Colored2<T> {
ColoredDimension2(T item) { super(item); } int getXO { return item x; } int getYO { return item.y, } int getZO { return item z; }
}
class Solid2<T extends Dimension & HasColor & Weight> extends ColoredDimension2<T> {