我们什么时候需要泛型 <> 符号?

When do we need generics <> notation?

我正在阅读 MinPQ 的实现,我对何时使用泛型表示法感到有点困惑。之前,我假设你只在与 class 相关时才这样做,例如在 return 类型中,或者在 Iterable<Key> 中的界面中。原来是这样,直到遇到下面的用法。

public class MinPQ<Key> implements Iterable<Key> { //I understand this
    public MinPQ(int initCapacity) {
        pq = (Key[]) new Object[initCapacity + 1];
        N = 0;
    }  //also clear

    public Iterator<Key> iterator() { return new HeapIterator(); }

    private class HeapIterator implements Iterator<Key> { 
    // Why does HeapIterator not need the generic notation here?

        private MinPQ<Key> copy;
        public HeapIterator() {
            if (comparator == null) 
                copy = new MinPQ<Key>(size());
                //where does this generics come from, no constructor is declared this way
            else                   
                copy = new MinPQ<Key>(size(), comparator);

            for (int i = 1; i <= N; i++)
                copy.insert(pq[i]);
        }
    }
}

为什么

private class HeapIterator implements Iterator<Key>

而不是

private class HeapIterator<Key> implements Iterator<Key>

另外,当我们调用构造函数时,class声明是否是它是否使用泛型的唯一决定因素?

这只是编写泛型的另一种方式,有时它会使代码变得简单一些,您不必考虑类型是什么。

private class HeapIterator <Type> implements Iterator <Type>  

使用上面的方法意味着这个class有类型参数并且在构造实例时必须指定它。

例如,要创建实例,您必须使用以下语句

HeapIterator <Key> itr = new HeapIterator<Key>();

private class HeapIterator implements Iterator<Key>

此 class 没有类型参数,"Key" 是有效类型,此 class 仅在编译时与此类型绑定。

例如,要创建实例,您必须使用以下语句

HeapIterator ir = new HeapIterator();

让我们从一个更简单的例子开始。考虑这个 class 声明:

public class IntIterator implements Iterator<Integer>

这是做什么的?它定义了一个实现 Iterator 的 class,但它只适用于 Integer 类型。这个 class 已经预定义了它在实现 next() 时将使用的类型,next() 将必须 return 一个 Integer 因为这是它声明的接口指定特定类型。

现在如果我想要一个更通用的迭代器怎么办?我可能会这样定义 class:

public class GeneralIterator<T> implements Iterator<T>

这看起来有些相似,但非常不同。在这里,我说过,"My GeneralIterator is a generic class. You have to tell me what type it works with, and all the Iterator methods will use the same type that you tell me to." 这里,GeneralIterator<T> 创建类型参数,而 Iterator<T> 使用 该类型参数。

现在让我们仔细看看您的示例。让我们从顶部 class 定义开始:

public class MinPQ<Key> implements Iterable<Key>

你说你懂这个。好的!你明白这就像我上面的 GeneralIterator<T> 例子。 MinPQ<Key> 创建 一个名为 Key 的类型参数,并声明它实现 Iterable using 该类型范围。所以现在我们留下了令人困惑的部分,内部 class 声明:

private class HeapIterator implements Iterator<Key>

这更像是我的第一个例子,但也有一点不同。它不是指定具体的 class,而是 重用 MinPG<Key> 创建的泛型类型参数。这是完全有道理的。 HeapIterator 不想创建其 own 类型参数;它想使用与外部 class 相同的一个!而这正是它正在做的。这意味着 HeapIterator 的实现使用与其外部 MinPG<Key>.

相同的类型

如果他们写了:

private class HeapIterator<Key> implements Iterator<Key>

那将是非常不同的。它会使 HeapIterator 创建一个 new 类型参数,该参数可能不同于 MinPG<Key>.

定义的类型参数

有了这个理解,在 HeapIterator 的构造函数中实例化一个新的 MinPG<Key> 就容易多了。这又是 使用 MinPG<Key> 定义的类型参数。它只是说 "This instance of MinPG has to use the same type as the original one," 而不是允许 HeapIterator class.

中的不同类型

TL;DR: 这一切都是为了确保所有对象的类型都匹配,并且不允许在类型不同的地方出现奇怪的事情。

我怀疑这里发生了两件事。一是 Java 支持原始类型...

Raw Types
To facilitate interfacing with non-generic legacy code, it is possible to use as a type
the erasure (§4.6) of a parameterized type (§4.5) or the erasure of an array type
(§10.1) whose element type is a parameterized type. Such a type is called a raw
type.
More precisely, a raw type is defined to be one of:
• The reference type that is formed by taking the name of a generic type declaration
without an accompanying type argument list.
• An array type whose element type is a raw type.
• A non-static member type of a raw type R that is not inherited from a superclass
or superinterface of R.
A non-generic class or interface type is not a raw type.
To see why a non-static type member of a raw type is considered raw, consider the
following example:
class Outer<T>{
 T t;
 class Inner {
 T setOuterT(T t1) { t = t1; return t; }
 }
}
The type of the member(s) of Inner depends on the type parameter of Outer. If Outer is
raw, Inner must be treated as raw as well, as there is no valid binding for T.

http://docs.oracle.com/javase/specs/jls/se7/jls7.pdf 这可以解释你的问题 1.

问题2可以用桥接法来解释

http://docs.oracle.com/javase/tutorial/java/generics/bridgeMethods.html

Bridge Methods

When compiling a class or interface that extends a parameterized class or implements a parameterized interface, the compiler may need to create a synthetic method, called a bridge method, as part of the type erasure process. You normally don't need to worry about bridge methods, but you might be puzzled if one appears in a stack trace.

你的问题很简单:

  1. 在示例 1 中,您的 class 不是通用的,它实现的通用参数 typeKey
  2. 在示例 2 中,您的 class 是通用的,它的通用参数 name 是 `Key'

假设您有一个名为 Key 的 class,hidingshadowing[ 中的通用参数名称 Key =26=] class 名字 Key;第二个例子的代码等同于:

private class HeapIterator<T> implements Iterator<T>