Java 泛型语法

12

我正在尝试理解以下内容的含义:

public class Bar<T extends Bar<T>> extends Foo<T> {
    //Some code
}

做这样的事情有什么好处(例如使用案例)?
5个回答

7
这是一个相当理论的例子,但在以下情况下可能会用到它:
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();
        }
    }
}

如果T不是Bar的子类,那么你将无法调用param.getValue()。但实际上,由于T继承了Bar,所以它是Bar的子类。

@maba 看起来我在半路上改变了主意;-) 已更正。 - assylias
2
请注意,Bar 应该是抽象的,因为它永远不能直接使用。请参见我的答案:https://dev59.com/vGs05IYBdhLWcg3wQvke#7355094 - Paul Bellora
1
@PaulBellora 的确。java.lang.Enum 的定义非常相似:public abstract class Enum<E extends Enum<E>> implements Comparable<E> - dcernahoschi
@dcernahoschi 那将是一个很棒的答案。刚刚看到你已经回答了。为此点赞。 - assylias
@PaulBellora 我并不排除其他子类(表达T应该有很多目的)。我是说这个类本身不一定要是抽象的。我的意思是它不像“永远不能直接使用”。 - Ben Schulz
显示剩余5条评论

4

这与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;
  }
}

由于这样定义的类必须是抽象的,不能直接实例化,所以该模式在类似于普通枚举扩展的构造中非常有用:

// 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》的示例。


3
你基本上是限制了 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扩展了Bar<T>,所以您可以这样引用它。


我可以随时调用doSomethingOnlyBarCanDo()方法,因为它和当前类在同一个类中。那这个方法有什么特别之处呢? - Amit Deshpande
1
@AmitD 只有当t是一个Bar对象时,你才能调用t.doSomethingOnlyBarCanDo(); - assylias
@学生 如果这样做可以使您的代码更易于维护和理解,或者隐藏不应向外部类公开的复杂内容,则通常是一个好习惯。我个人喜欢对泛型设置限制,因为它可以让外部开发人员更好地了解该类的预期用途。真正混淆视听的一件事是学术界将“Foo”和“Bar”用作合法的类名。它们并没有提供任何关于这些类应该做什么的提示,只告诉您语法能够做什么。 - CodeBlind
@assylias,我对那个例子不满意,所以我提供了我的例子。:) - Amit Deshpande

2

一个典型的用例是包装器/修饰器模式。 Bar 可能会包装扩展了 Bar 本身的 TFoo 是一个处理 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 
    }
}

2
这也适用于“public class Bar<T> extends Foo<T>”。 - assylias

0
唯一要说的是使用<T extends Bar<T>>,你告诉程序现在T的类型为Bar<T>,这意味着TBar,因此您可以访问所有可用于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);
    }
}

你的例子有点不对,因为在 Bar 类中,你总是可以访问到 Bar 类的方法,所以告诉 T 是 Bar 类型并没有真正的优势。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接