Java 中的 <> 是什么意思?

6

我知道如果你想创建一个列表(例如),你可以这样做:

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

}


它指定了一个类型参数。您可以在此处,有关Java教程的相关部分阅读更多关于泛型的内容。 - obataku
那么…这是因为在通用类型中存在静态方法而导致的糟糕代码吗?在那里存在的潜在危险是什么?问题是我可以将多个类型压入列表中,还是会给我一个编译错误? - GenericJam
1
C#和Java中的泛型与C++中的模板有什么区别? - Aaron Kurtzhals
@GenericJam,我认为在泛型类型中使用静态方法没有任何问题。但是,我确实发现你的getList实现有问题,因为它仅返回一个LinkList,其元素是数组中的最后一个,并且相邻的邻居为空。 - obataku
@AaronKurtzhals 这篇文章的第一个答案非常好地解释了泛型,但我也想知道为什么在泛型类型中使用静态方法是危险的(?)。你必须通过在 static 后面加上 '<T>' 来欺骗编译器,否则就需要强制转换?我不完全理解它为什么能够工作。 - GenericJam
4个回答

11

<> 帮助编译器验证类型安全性。

编译器在编译时确保 List<MyObj> 仅保持类型为 MyObj 的对象,而不是在运行时。

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


1
对于容器,您可以将其读作“的”,例如List<String>是一个字符串列表。 - Jason Sperske
应该注意,这仅是一个编译时指令。由于类型擦除,运行时不知道列表中应该有什么类型。 - Chris Thompson
@ChrisThompson:你太快了。我正在更新我的答案,加上那些细节。 - kosa
@thinksteep 哈哈,我刚刚注意到了,还是+1。 平均开发人员似乎并不在意这个。 - Chris Thompson

6

当你在<>中放入一个类型时,它用于将潜在的运行时异常转换为编译错误。

以没有泛型的代码为例:

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

// 这段代码能够编译通过,但在运行时会出现错误,因为它将String类型与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 - cHao
这是泛型如何工作的一个很好的例子。还应该注意到,泛型不仅限于容器。这只是泛型最常见的用法。 - Code-Apprentice

3

<>被用于一个酷炫的功能,叫做泛型

在Java 5之前,泛型并不存在。像ArrayList这样的集合会返回和操作Objects。这就存在问题,比如你知道你只会储存String,但是如果你在所有类中都使用Objects,不仅必须使用恼人的强制类型转换(String blah = (String) list.get(9);),而且如果你错误地将一个Integer放入列表中,你的程序会抛出ClassCastException并烧毁。

Java 5通过引入泛型解决了这个问题,现在你可以说ArrayList<String>来表示你只会在这个ArrayList中使用String。但是如果你需要创建一个ArrayList<Supercalifragilisticexpealidocious>呢?显然,打那么长的字不是一件愉快的经历,特别是当你必须输入 

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

声明其中之一。此外,这可能会导致拼写错误,特别是对于这种类型参数; 在某些情况下,您必定会拼错一个或两个字母并使程序无法找到符号而烧毁 - 或者更糟糕的是,静默使用错误的类并导致逻辑错误。

Java 7引入了简单的<>语法。它被称为钻石操作符,使您不必在赋值运算符右侧重新输入类型参数(<>内部的内容)。因此,

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

变成

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

这意味着不用花费大量时间重新输入“ Supercalifragilisticexpealidocious ”。希望这能帮到你!

1

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


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