Я пытаюсь понять, что означает следующее:
public class Bar<T extends Bar<T>> extends Foo<T> {
//Some code
}
В чем преимущество того, что вы делаете что-то вроде этого (например, прецедент?).
Я пытаюсь понять, что означает следующее:
public class Bar<T extends Bar<T>> extends Foo<T> {
//Some code
}
В чем преимущество того, что вы делаете что-то вроде этого (например, прецедент?).
Это довольно теоретический пример, но в этом случае он может понадобиться:
public class Bar<T extends Bar<T>> extends Foo<T> {
private final T value;
public T getValue() { return value; }
public void method(T param) {
if (param.getValue().equals(someValue)) {
doSomething();
} else {
doSomethingElse();
}
}
}
Вы не смогли бы вызвать param.getValue()
, если T не был Bar
, потому что T extends Bar<T>
.
Это очень похоже на способ определения класса java.lang.Enum
:
public abstract class Enum<E extends Enum<E>> implements Comparable<E> {
private final String name;
private final int ordinal;
protected Enum(String name, int ordinal) {
this.name = name; this.ordinal = ordinal;
}
public final String name() { return name; }
public final int ordinal() { return ordinal; }
public String toString() { return name; }
public final int compareTo(E o) {
return ordinal - o.ordinal;
}
}
Поскольку классы, определенные таким образом, должны быть abstract и не могут быть созданы напрямую, шаблон полезен в конструкциях, аналогичных тому, как расширяются обычные перечисления:
// corresponds to
// enum Season { WINTER, SPRING, SUMMER, FALL }
final class Season extends Enum<Season> {
private Season(String name, int ordinal) { super(name,ordinal); }
public static final Season WINTER = new Season("WINTER",0);
public static final Season SPRING = new Season("SPRING",1);
public static final Season SUMMER = new Season("SUMMER",2);
public static final Season FALL = new Season("FALL",3);
private static final Season[] VALUES = { WINTER, SPRING, SUMMER, FALL };
public static Season[] values() { return VALUES.clone(); }
public static Season valueOf(String name) {
for (Season e : VALUES) if (e.name().equals(name)) return e;
throw new IllegalArgumentException();
}
}
Пример из книги "Java Generics and Collection".
В основном вы ограничиваете типы типов, которые Bar
будет "обрабатывать" на все, что расширяет Bar<T>
. В этом случае вы хотите убедиться, что Bar
имеет дело только с расширениями самого себя - возможно, вызовите метод, который закрыт для Bar
. Один простой пример состоит в том, что вы можете использовать этот класс для реализации своего рода связанного списка и повторять его, выполняя то, что может/должно делать только Bar
. Скажем, у вас есть следующий код:
public class Bar<T extends Bar<T>> extends Foo<T> {
private T next;
//Initialize next in constructor or somewhere else
private void doSomethingOnlyBarCanDo(){
//Do it...
}
public void iterate(){
T t = next;
while(t != null){
t.doSomethingOnlyBarCanDo();
t = t.next;
}
}
}
С помощью такого типа конструкции было бы очень легко перебирать "цепочку" экземпляров Bar
, потому что каждый экземпляр имел бы ссылку на следующий - напомните, что T
extends Bar<T>
, поэтому вы можете ссылаться на него как таковой.
Один типичный вариант использования - шаблон Wrapper/Decorator. Bar
возможно завершает T
, который расширяет Bar
сам. Foo
- это просто параметризованный класс, который имеет дело с T
.
public abstract class Foo<T> {
public abstract void foo(T t);
}
public class Bar<T extends Bar<T>> extends Foo<T> {
private T wrapped;
public Bar(T w) {
this.wrapped = w;
}
public abstract void foo(T t) {
// do something
}
}
Единственное, что я мог сказать: <T extends Bar<T>>
, используя это, вы говорите, что T
теперь имеет тип Bar<T>
, что означает, что T
есть Bar
, чтобы вы могли получить доступ ко всем методам, доступным для Bar
, если вы только делаете Bar<T>
, вы не сможете получить к нему доступ, так как T
был бы общим.
Что такое использование?
Вы можете использовать это для композиции. например.
public class CustomList<T extends List<T>> {
T t;
T get(int index) {
//Some custom code here
return t.get(index);
}
}
Ваш пример неверен, поскольку нет реального преимущества Telling, что T имеет Type Bar, поскольку у вас всегда будет доступ к методам класса Bar внутри класса Bar