Эккель Брюс
Шрифт:
//: generics/ClassTypeCapture.java
class Building {}
class House extends Building {}
public class ClassTypeCapture<T> { Class<T> kind;
public ClassTypeCapture(Class<T> kind) { this.kind = kind;
}
public boolean f(Object arg) {
return kind.islnstance(arg);
}
public static void main(String[] args) { ClassTypeCapture<Building> cttl =
new CIassTypeCapture<Bui1di ng>(Bui 1di ng.class); System.out.pri nt1n(cttl.f(new Bui 1di ng)); System.out.pri ntin(cttl.f(new House)); ClassTypeCapture<House> ctt2 =
new ClassTypeCapture<House>(House.class); System.out.pri nt1n(ctt2.f(new Bui 1di ng)); System.out.pri nt1n(ctt2.f(new House));
}
} /* Output; true true false true *///:-
Компилятор следит за тем, чтобы метка типа соответствовала обобщенному аргументу.
Создание экземпляров типов
Попытка создания newT в Erased.java не работает отчасти из-за стирания, а отчасти из-за того, что компилятор не может убедиться в наличии у Т конструктора по умолчанию (без аргументов). Но в С++ эта операция естественна, прямолинейна и безопасна (проверка выполняется во время компиляции):
//; generics/InstantiateGenericType.java import static net.mindview.util.Print.*;
class ClassAsFactory<T> { T x;
public ClassAsFactory(Class<T> kind) {
try { продолжение &
х = kind.newInstanceO; } catch(Exception е) {
throw new RuntimeException(e);
}
}
}
class Employee {}
public class InstantiateGenericType {
public static void main(String[] args) { ClassAsFactory<Employee> fe =
new ClassAsFactory<Employee>(Employee.class); pri nt("ClassAsFactory<Employee> успех"); try {
ClassAsFactory<Integer> fi =
new ClassAsFactory<Integer>(Integer.class); } catch(Exception e) {
print("ClassAsFactory<Integer> неудача");
}
}
} /* Output:
ClassAsFactory<Employee> успех ClassAsFactory<Integer> неудача *///:-
Программа компилируется, но с ClassAsFactory<Integer> происходит сбой, так как Integer не имеет конструктора по умолчанию. Ошибка не обнаруживается во время компиляции, поэтому специалисты из Sun считают такие решения нежелательными. Вместо этого рекомендуется использовать явную фабрику и ограничивать тип, чтобы принимался только класс, реализующий эту фабрику:
//: generics/FactoryConstraint.java
interface FactoryI<T> { T createO;
}
class Foo2<T> { private T x:
public <F extends FactoryI<T>> Foo2(F factory) { x = factory. createO;
}
// ...
class IntegerFactory implements FactoryI<Integer> { public Integer createO {
return new Integer(O):
}
}
class Widget {
public static class Factory implements FactoryI<Widget> public Widget createO {
return new Widget О:
public class FactoryConstraint {
public static void main(String[] args) {
new Foo2<Integer>(new IntegerFactoryO); new Foo2<Widget>(new Widget.FactoryO);
}
} ///:-
В сущности, это всего лишь разновидность передачи Class<T>. В обоих вариантах передаются объекты фабрик; просто в случае с Class<T> объект фабрики оказывается встроенным, а при предыдущем решении он создается явно. Тем не менее в обоих случаях реализуется проверка времени компиляции.
Другое решение основано на использовании паттерна «шаблонный метод». В следующем примере get — шаблонный метод, a create определяется в субклассе для получения объекта этого типа:
//: generics/CreatorGeneriс.java
abstract class GenericWithCreate<T> { final T element;
GenericWithCreateO { element = createO; } abstract T createO;
}
class X {}
class Creator extends GenericWithCreate<X> { X createO { return new XO; } void fO {
System.out.pri nt 1 n(el ement.getClass.getSi mpleName),
}
}
public class CreatorGeneric {
public static void main(String[] args) { Creator с = new CreatorO: C.fO;
}
} /* Output: X
*///:-
Массивы параметризованных типов
Как мы видели в Erased.java, создавать массивы параметризованных типов нельзя. Везде, где возникает необходимость в создании таких массивов, следует применять ArrayList:
//: generics/Li stOfGeneri cs.java import java.util.*;