简体   繁体   中英

What is the point of making a method generic when it has only one generic type anyway?

If a method is defined as:

<T> List<T> transform(List<T> list)

What does the first T add? As it is essentially the same as:

List<T> transform(List<T> list)

<T> is the definition. You need to define generic type parameters before you use them, just like you must define variables before you use them.


Remember, T is just a naming convention. That identifier can be anything. It could be <F> or <Foo> .

Suppose they implemented it how you suggest, and the compiler just infers any missing type to be a generic type parameter. Suppose I have a custom class Foo which I forgot to import.

List<Foo> transform(List<Foo> list)

The compiler would say " Ok, I don't know what Foo is, so let's just assume it's a generic parameter ". Your method then compiles just fine, when you would much prefer that the compiler had caught that mistake.

Or maybe you think T should be a special case. You would then be taking something that's currently just a convention and making that a special part of the language. You'd have to deal with the possibility of someone creating a real class called T .

So explicit declarations have their uses, even if they may seem verbose sometimes.

The difference is the scope of T . It can be defined on the class level or for each individual method.

List<T> transform(List<T> list)

This one returns and accepts List<T> where T is the same for all methods.

<T> List<T> transform(List<T> list)

This method has its own T unrelated to the generic type of its class (if any).

 <T> List<T> transform(List<T> list)

As it is essentially the same as:

 List<T> transform(List<T> list)

It's not.

In the second case, T has to have been defined on the containing class:

class GenericClass<T> {
  List<T> transform(List<T> list) { ... }
}

and you can then only pass in/get back a list of the same element type as the class:

GenericClass<String> genericClass = new GenericClass<>();
List<String> stringList = genericClass.transform(Arrays.asList(""));  // OK.
List<Integer> intList = genericClass.transform(Arrays.asList(0));  // Error!

However, with the first form, the T isn't defined on the class, so it can be different on each invocation:

class NonGenericClass {
  <T> List<T> transform(List<T> list) { ... }
}

NonGenericClass nonGenericClass = new NonGenericClass();
List<String> stringList = nonGenericClass.transform(Arrays.asList(""));  // OK.
List<Integer> intList = nonGenericClass.transform(Arrays.asList(0));  // OK.

It is not the same at all.

The <T> declares the variable, the rest is the use of the variable. This straight up fails to compile:

public class Example {
    public List<T> transform(List<T> foo) {}
}

try it.

In case this is what you have:

public class Example<T> {
    public <T> List<T> transform(List<T> foo) {}
}

Then you have 2 unrelated type variables, both unfortunately named T , which is extremely confusing; I strongly suggest you don't do that (the class has defined a and then the method defines its own T, ensuring that all uses of T in that method declaration are assumed to be the defined on the method; the defined by the class is no longer accessible; it has been 'shadowed'. It is in that sense equivalent to this:

public class Example {
    int x;

    public void foo(int x) { }
}

The int x of foo is utterly unrelated to the int x; field, but because they have the same name, you have made it impossible to refer to the field named x . At least for this there's an escape ( this.x still refers to the field) - no such luxury with generics, so, do not shadow variables like this.

It is somewhat unlikely you want a type var on the method at all if you have this code; but if you really do, don't use T, but use anything else:

public class Foo<T> {
    public <Y> List<Y> transform(List<Y> foo) {}
}

As was already said, the following are essentially identical.

<T> List<T> transform(List<T> list)

List<T> transform(List<T> list)

But the former also allows one to further constrain T. For example you can do

<T extends Foo> List<T> transform(List<T> list) {
...   
}

You can only pass a List<T> if T subclasses Foo.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM