【问题标题】:What does <> do in Java?<> 在 Java 中做了什么?
【发布时间】:2012-08-07 03:50:00
【问题描述】:

我知道如果您想要一个列表(例如),您可以像这样创建它:

List<String>

如果你想创建一个通用类型的列表,你可以这样做:

MyList<T>

那么 所做的唯一事情就是将对象与容器或列表耦合起来吗?它还有其他用途吗?它实际上是做什么的? 在另一篇文章中阅读如何将静态方法放入泛型类型对类型安全来说是一件坏事,所以这是糟糕的代码吗?

public class LinkList<T> {

private final T t;
private final LinkList<T> next;

public LinkList(T t, LinkList<T> next){
    this.t = t;

    this.next = next;
}
// Creates list from array of T
public static <T> LinkList<T> getList(T[] t){
    if(t == null){
        return null;
    }
    LinkList linkList = null;
    for(int i = t.length-1; i >= 0; i--){
        linkList = new LinkList(t[i], linkList);
    }
    return linkList;
}

public T element() {
    return t;
}

public LinkList<T> getNext() {
    return next;
}

}

【问题讨论】:

  • 它指定了一个类型参数。您可以阅读有关泛型的更多信息here, in the relevant Java Tutorial。
  • 那么......这是因为泛型类型中的静态方法而导致的错误代码吗?在那里有什么潜在的危险?问题是我可以将多个类型塞入列表中还是会给我一个编译错误?
  • @GenericJam 我认为在泛型类型中使用静态方法没有任何问题。不过,我确实在您的 getList 实现中看到了一些问题,因为它仅返回一个 LinkList,其元素是数组中的最后一个元素,并且其相邻邻居为空。
  • @AaronKurtzhals 该帖子中的第一个答案很好地解释了泛型,但我也想知道为什么在泛型类型中使用静态方法很危险(?)。您必须通过在静态之后放置“”来欺骗编译器而不是抱怨,还是强制转换?我不完全理解它为什么起作用。

标签: java list generics types


【解决方案1】:

&lt;&gt; 帮助编译器验证类型安全。

编译器确保List&lt;MyObj&gt; 在编译时而不是运行时保存MyObj 类型的对象。

泛型主要用于编译时的类型安全。由于类型擦除,编译后所有泛型信息将替换为具体类型。

【讨论】:

  • 对于容器,您可以将其读作“of”,例如 List 是字符串列表
  • 需要注意的是它只是一个编译时指令。由于类型擦除,运行时不知道列表中应该包含什么类型。
  • @ChrisThompson:你太快了。我正在用这些细节更新我的答案。
  • @thinksteep haha​​,我刚刚注意到,+1 都一样。并不是说它对普通开发人员真的很重要......
【解决方案2】:

当您将&lt;&gt; 和一个类型放入其中时,它用于将潜在的运行时异常转换为编译错误。

以这段代码为例,没有泛型

ArrayList stringList = new ArrayList();
stringList.add("string");
stringList.add(3.4);
String s = (String) stringList.get(1);

// 这将编译并产生运行时错误,将字符串比较为 double。

如果你添加泛型,你可能会在编写时发现这些错误。

考虑以下代码:

ArrayList<String> stringList = new ArrayList<String>(); // Since Java 7 you can write - new ArrayList<>()
stringList.add("string"); // OK
stringList.add(3.4); // Would not compile!

这样您可以在编译时捕获与类型相关的错误。

编译器本身并不关心你是否使用了泛型。它会在编译时删除所有这些,并且就像您没有使用泛型一样。但是,如果您首先遇到编译错误,它不会让您编译。

我还注意到我没有回答您关于代码的问题。

当你做这样的事情时

class LinkedList<T> {
....
.... 
}

你告诉编译器这个类支持泛型,在这种情况下,可以做我上面所说的

你可以的

LinkedList<String> list = new LinkedList<String>();

现在,在你的班级中,无论在哪里显示 T,它都会表现得好像是 String,因此只允许添加和维护字符串。

【讨论】:

  • 你确定那个运行时错误?我以为它只会返回false...事实上,我认为这是equals 合约的一部分,与不同类型的对象相比只会返回false。
  • 这是泛型工作原理的一个很好的例子。还应该注意,泛型不限于容器。这些恰好是泛型最常见的用法。
【解决方案3】:

&lt;&gt; 用于一个很酷的功能,称为 generics。

在 Java 5 之前,不存在泛型。像ArrayList 这样的集合返回并操纵了Objects。问题在于,例如,当您知道您只会存储 Strings 时。但是,如果您在所有课程中都使用Objects,您不仅必须使用烦人的强制转换(String blah = (String) list.get(9);),而且如果您出错并在列表中添加Integer,您的程序将@987654328 @然后烧掉。

Java 5 用泛型解决了这个问题,所以现在你可以说ArrayList&lt;String&gt; 来表示你只会在这个ArrayList 中使用Strings。但是如果你需要ArrayList&lt;Supercalifragilisticexpealidocious&gt;呢?显然,打字并不是一种愉快的体验,尤其是当你不得不打字的时候

ArrayList<Supercalifragilisticexpealidocious> supercaliList = new ArrayList<Supercalifragilisticexpealidocious>();

声明其中之一。此外,它可能会导致拼写错误,尤其是对于这种大小的类型参数;在某些时候,你一定会打错一两个字母,让你的程序cannot find symbol 烧毁——或者更糟的是,默默地使用错误的类并导致逻辑错误。

Java 7 引入了普通的&lt;&gt; 语法。它被称为 菱形运算符,因此您不必在赋值运算符的右侧重新键入类型参数(&lt;&gt; 中的内容)。因此,

ArrayList<Supercalifragilisticexpealidocious> supercaliList = new ArrayList<Supercalifragilisticexpealidocious>();

变成

ArrayList<Supercalifragilisticexpealidocious> supercaliList = new ArrayList<>();

这意味着重新输入Supercalifragilisticexpealidocious 的时间大大减少。

希望这会有所帮助!

【讨论】:

    【解决方案4】:

    可以与您希望的任何类一起使用,而不仅仅是容器。这些只是最常见的,因为我们希望能够在容器中存储我们想要的任何类型的对象,并且仍然保持类型安全。要更深入地理解这一点,您应该研究泛型及其用途。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-04-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-10-27
      • 1970-01-01
      • 1970-01-01
      • 2021-09-05
      相关资源
      最近更新 更多