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

时间:2015-02-04 03:34:35

标签: java generics

我正在阅读MinPQ的实现,我对何时使用泛型表示法感到困惑。在此之前,我假设您只在与类相关时才这样做,例如在返回类型中,或在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>

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

4 个答案:

答案 0 :(得分:2)

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

public class IntIterator implements Iterator<Integer>

这是做什么的?它定义了一个实现Iterator的类,但它仅适用于Integer类型。此类预定义了它在实现next()时将使用的类型,next()必须返回Integer,因为它是通过指定特定类型声明的接口

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

public class GeneralIterator<T> implements Iterator<T>

这看起来有些相似,但它的非常不同。在这里,我已经说过,&#34;我的GeneralIterator是一个通用类。您必须告诉我它的工作类型,并且所有Iterator方法都将使用您告诉我的相同类型。&#34;在这里,GeneralIterator<T>创建了type参数,Iterator<T>使用该类型参数。

现在让我们仔细看看你的例子。让我们从顶级定义开始:

public class MinPQ<Key> implements Iterable<Key>

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

private class HeapIterator implements Iterator<Key>

这更像是我的第一个例子,但它也有点不同。它不是指定具体的类,而是重用MinPG<Key> 创建的泛型类型参数。这很有道理。 HeapIterator并不想创建自己的类型参数;它想要使用与外层相同的一个!这正是它正在做的事情。所有这些意味着HeapIterator的实施使用与其外MinPG<Key>相同的类型。

如果他们写了:

private class HeapIterator<Key> implements Iterator<Key>

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

有了这种理解,MinPG<Key>构造函数中新HeapIterator的实例化会更容易一些。这又是使用 MinPG<Key>定义的类型参数。它只是说,&#34; MinPG的这个实例必须使用与原始实例相同的类型,&#34;而不是在HeapIterator类中允许不同的类型。

TL; DR:这就是确保所有对象的类型匹配,并且不允许类型不同的奇怪事物。

答案 1 :(得分:0)

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

private class HeapIterator <Type> implements Iterator <Type>  

使用上述方法意味着此类具有类型参数,并且在构造实例时必须指定它。

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

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

private class HeapIterator implements Iterator<Key>

此课程没有类型参数和&#34; Key&#34;是有效类型,此类仅在编译时使用此类型绑定。

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

HeapIterator ir = new HeapIterator();

答案 2 :(得分:0)

我怀疑这里有两件事情。一个是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.

答案 3 :(得分:0)

你的问题很简单:

  1. 在示例1中,您的类不是通用的,它实现的泛型参数 type Key
  2. 在示例2中,您的类是通用的,其泛型参数 name 是“Key”
  3. 假设您有一个名为Key的类,隐藏阴影类名Key中的通用参数名称Key ;第二个例子的代码与:

    相同
    private class HeapIterator<T> implements Iterator<T>