<T>列表<?扩展T> f()一个有用的签名?它/使用它有什么问题吗?

use*_*718 35 java generics signature

<T> List<? extends T> f()签名是否有用?它/使用它有什么问题吗?

这是一个面试问题.我知道这个:

  1. 它汇编很好
  2. 使用它List<? extends Number> lst = obj.<Number>f(),然后我只能在lst上调用那些在签名中不包含T的List方法(比如isEmpty(),size(),但不能添加(T),删除(T)

这完全回答了这个问题吗?

And*_*kin 22

这个方法签名是"有用的",因为你可以用它实现非平凡的,非退化的方法(也就是说,返回null和抛出错误不是你唯一的选择).如下面的示例所示,这种方法可用于实现一些代数结构,例如幺半群.

首先,观察这List<? extends T>是一个具有以下属性的类型:

  • 您知道此列表的所有元素都符合类型T,因此每当从此列表中提取元素时,您都可以在T期望a的位置使用它.您可以从此列表中读取.
  • 确切的类型是未知的,因此您永远无法确定T可以将特定子类型的实例添加到此列表中.也就是说,你实际上无法将新元素添加到这样的列表中(除非你使用nulls/type casts/exploit Java的类型系统的不健全,即).

结合起来,这意味着它List<? extends T>类似于受追加保护的列表,具有类型级别的附加保护.

实际上,您可以使用此类"附加保护"列表进行有意义的计算.这里有一些例子:

  • 您可以使用单个元素创建附加保护的列表:

    public static <T> List<? extends T> pure(T t) {
      List<T> result = new LinkedList<T>();
      result.add(t);
      return result;
    }
    
    Run Code Online (Sandbox Code Playgroud)
  • 您可以从普通列表创建追加保护的列表:

    public static <T> List<? extends T> toAppendProtected(List<T> original) {
      List<T> result = new LinkedList<T>();
      result.addAll(original);
      return result;
    }
    
    Run Code Online (Sandbox Code Playgroud)
  • 您可以组合追加保护的列表:

    public static <T> List<? extends T> combineAppendProtected(
      List<? extends T> a,
      List<? extends T> b
    ) {
      List<T> result = new LinkedList<T>();
      result.addAll(a);
      result.addAll(b);
      return result;
    }
    
    Run Code Online (Sandbox Code Playgroud)
  • 而且,最重要的是,对于这个问题,您可以实现一个返回给定类型的空追加保护列表的方法:

    public static <T> List<? extends T> emptyAppendProtected() {
      return new LinkedList<T>();
    }
    
    Run Code Online (Sandbox Code Playgroud)

在一起,combineempty形成实际的代数结构(幺),和方法,如pure保证它不变质(即,它具有更多的元素只是一个空表).实际上,如果你有一个类似于通常的Monoid类型类的接口:

  public static interface Monoid<X> {
    X empty();
    X combine(X a, X b);
  }
Run Code Online (Sandbox Code Playgroud)

然后你可以使用上面的方法来实现它如下:

  public static <T> Monoid<List<? extends T>> appendProtectedListsMonoid() {
    return new Monoid<List<? extends T>>() {
      public List<? extends T> empty() {
        return ReadOnlyLists.<T>emptyAppendProtected();
      }

      public List<? extends T> combine(
        List<? extends T> a,
        List<? extends T> b
      ) {
        return combineAppendProtected(a, b);
      }
    };
  }
Run Code Online (Sandbox Code Playgroud)

这表明在您的问题中给出签名的方法可用于实现一些常见的设计模式/代数结构(monoids).不可否认,这个例子有点人为,你可能不想在实践中使用它,因为你不想太过惊讶你的API用户.


完整的可编辑示例:

import java.util.*;

class AppendProtectedLists {

  public static <T> List<? extends T> emptyAppendProtected() {
    return new LinkedList<T>();
  }

  public static <T> List<? extends T> combineAppendProtected(
    List<? extends T> a,
    List<? extends T> b
  ) {
    List<T> result = new LinkedList<T>();
    result.addAll(a);
    result.addAll(b);
    return result;
  }

  public static <T> List<? extends T> toAppendProtected(List<T> original) {
    List<T> result = new LinkedList<T>();
    result.addAll(original);
    return result;
  }

  public static <T> List<? extends T> pure(T t) {
    List<T> result = new LinkedList<T>();
    result.add(t);
    return result;
  }

  public static interface Monoid<X> {
    X empty();
    X combine(X a, X b);
  }

  public static <T> Monoid<List<? extends T>> appendProtectedListsMonoid() {
    return new Monoid<List<? extends T>>() {
      public List<? extends T> empty() {
        return AppendProtectedLists.<T>emptyAppendProtected();
      }

      public List<? extends T> combine(
        List<? extends T> a,
        List<? extends T> b
      ) {
        return combineAppendProtected(a, b);
      }
    };
  }

  public static void main(String[] args) {
    Monoid<List<? extends String>> monoid = appendProtectedListsMonoid();
    List<? extends String> e = monoid.empty();
    // e.add("hi"); // refuses to compile, which is good: write protection!
    List<? extends String> a = pure("a");
    List<? extends String> b = pure("b");
    List<? extends String> c = monoid.combine(e, monoid.combine(a, b));
    System.out.println(c); // output: [a, b]
  }

}
Run Code Online (Sandbox Code Playgroud)


And*_*ner 15

我将"它是一个有用的签名"解释为"你能想到它的用例吗".

T 在调用站点确定,而不是在方法内部,因此只能从方法返回两件事:null或空列表.

鉴于您可以在与调用此方法一样多的代码中创建这两个值,因此没有充分的理由使用它.


实际上,另一个可以安全返回的值是一个列表,其中所有元素都为null.但是这也没用,因为你只能调用从返回值中添加或删除文字null的方法,因为? extends在类型绑定中.所以你所拥有的只是计算它包含的空数的东西.哪个也没用.