Why is it wrong to supply type parameter in the co

2019-02-19 13:18发布

I'm just learning about generics in Java from a textbook, where it talks about a class GenericStack<E> implemented with an ArrayList<E>.

Since in order to create a stack of strings, you use

GenericStack<String> = new GenericStack<String>()

or

GenericStack<String> new GenericStack<>()

therefore shouldn't the constructor of GenericStack be defined as public GenericStack<E>(), or public GenericStack<>()? The answer is no. It should be defined as public GenericStack().

Why is this? Obviously the constructor can easily infer the type from the class declaration, but given the verbosity of Java, I'm a bit befuddled why the <E> or simply the <> formalism is completely gotten rid of here.

3条回答
再贱就再见
2楼-- · 2019-02-19 13:22

You are confusing a method's declaration with its call site (that is, where you use it). If you want to use a generic that's declared on the class, only ever need to provide the <E> or <> at the call site — not the declaration. And in that regard, a constructor is declared like any other method:

public GenericStack<E> {
    public E get() { ...            // correct
    public <E> put(E element) { ... // bad (see below)
    public GenericStack() { ...     // correct, like the get()

Redeclaring the generic (as in the put case above) actually shadows it; the E in put(E) is actually a different E than in GenericStack<E>, despite having the same name. This is confusing, and you should avoid doing it.

查看更多
贪生不怕死
3楼-- · 2019-02-19 13:34

There are two parts to this:

  1. The generic type parameter given for the class definition is available within the class itself.

  2. You can have generic types specific to individual methods (including constructor).

Look at the following example:

package snippet;

import java.util.ArrayList;

public class Y<E> extends ArrayList<E> {

    public <T> Y(T t) {
    }
}

Where as the type E is available to the whole of the class, the type T is valid only within the constructor.

查看更多
祖国的老花朵
4楼-- · 2019-02-19 13:39

Both the class and the constructor can be generic on their own, i.e. each has its own type parameters. For example

class A<T>
{
    <S>A(S s){}
}

To invoke the constructor with all explicit type arguments

new <String>A<Integer>("abc");  // T=Integer, S=String

Think of it like this, A<Integer> is the concrete class, and <String>A is the concrete constructor.

Because of type inference, we can omit the type argument for the constructor

new A<Integer>("abc");  // S=String is inferred

The type argument for the class can also be omitted and inferred, by "diamond" (<>) syntax

A<Integer> a = new A<>("abc");  // T=Integer is inferred, as well as S=String

I wrote a (long) post on this subject - https://stackoverflow.com/a/32292707/2158288

查看更多
登录 后发表回答