Ограничения, уже упоминавшиеся ранее в этой главе, сужают круг параметров типов, используемых при параметризации. Хотя это позволяет предъявлять требования к типам, к которым применяется ваш параметризованный код, у ограничений имеется и другой, потенциально более важный эффект: возможность вызова методов, определенных в ограничивающих типах. Поскольку стирание уничтожает информацию о типе, при отсутствии ограничений для параметров типов могут вызываться только методы Object. Но, если ограничить параметр подмножеством типов, вы сможете вызвать методы из этого подмножества. Для установления ограничений в Java используется ключевое слово extends. Важно понимать, что в контексте параметризации extends имеет совершенно иной смысл, нежели в обычной ситуации. Следующий пример демонстрирует основы установления ограничений:
Вероятно, вы заметили, что пример BasicBounds.java содержат некоторую избыточность, которая может быть устранена посредством наследования. С каждым уровнем наследования добавляются новые ограничения:
//: generics/InheritBounds.java
class HoldItem<T> {
T item;
HoldItem(T item) { this.item = item; }
T getItem() { return item; }
}
class Colored2<T extends HasColor> extends HoldItem<T> {
class ColoredDimension2<T extends Dimension & HasColor>
extends Colored2<T> {
ColoredDimension2(T item) { super(item); }
int getX() { return item.x; }
int getY() { return item.y; }
int getZ() { return item.z; }
}
class Solid2<T extends Dimension & HasColor & Weight>
extends ColoredDimension2<T> {
Solid2(T item) { super(item); }
int weight() { return item.weight(); }
}
public class InheritBounds {
public static void main(String[] args) {
Solid2<Bounded> solid2 =
new Solid2<Bounded>(new Bounded());
solid2.color();
solid2.getY();
solid2.weight();
}
}
HoldItem просто хранит объект; это поведение наследуется классом Colored2, который также требует, чтобы его параметр реализовывал HasColor. ColoredDimension2 и Solid2 продолжают расширение иерархии и добавляют на каждом уровне новые ограничения. Теперь методы наследуются, и их не нужно повторять в каждом классе. Пример с большим количеством уровней:
//: generics/EpicBattle.java
// Demonstrating bounds in Java generics.
import java.util.*;
interface SuperPower {}
interface XRayVision extends SuperPower {
void seeThroughWalls();
}
interface SuperHearing extends SuperPower {
void hearSubtleNoises();
}
interface SuperSmell extends SuperPower {
void trackBySmell();
}
class SuperHero<POWER extends SuperPower> {
POWER power;
SuperHero(POWER power) { this.power = power; }
POWER getPower() { return power; }
}
class SuperSleuth<POWER extends XRayVision>
extends SuperHero<POWER> {
SuperSleuth(POWER power) { super(power); }
void see() { power.seeThroughWalls(); }
}
class CanineHero<POWER extends SuperHearing & SuperSmell>
extends SuperHero<POWER> {
CanineHero(POWER power) { super(power); }
void hear() { power.hearSubtleNoises(); }
void smell() { power.trackBySmell(); }
}
class SuperHearSmell implements SuperHearing, SuperSmell {