0

I have the following class which builds:

public class Test<T> {
    public T DoSomething(T value) {
        return value;
    }
}

I can also define it like this class like this (notice the extra in the DoSomething signature (which also builds):

public class Test<T> {
    public <T> T DoSomething(T value) {
        return value;
    }
}

What is its purpose and when do I need to include it? I am asking about the additional <T> in the return type, not what generics are.

Turing85
  • 18,217
  • 7
  • 33
  • 58
Mike Cheel
  • 12,626
  • 10
  • 72
  • 101
  • 3
    This is a [generic method (`docs.oracle.com`)](https://docs.oracle.com/javase/tutorial/extra/generics/methods.html). – Turing85 Apr 28 '22 at 22:12
  • 1
    Does this answer your question? [What are Generics in Java?](https://stackoverflow.com/questions/7815528/what-are-generics-in-java) – Mushroomator Apr 28 '22 at 22:13
  • @Mushroomator I'm asking about the additional . I come from a C# background and we don't need that. – Mike Cheel Apr 28 '22 at 22:15
  • 1
    @MikeCheel please check the first duplicate linked. It discusses the exact case. Also, writing in ALL CAPS is generally regarded as rude/aggressive. – Turing85 Apr 28 '22 at 22:18
  • 1
    It's a different type variable; the programmer poorly chose the name T for two different things. – passer-by Apr 28 '22 at 22:18
  • @Turing85 It seems folks rushed to close the question without reading it. As I said I know what generics are but don't understand the necessity for the additional notation in the return. Sorry for the ALL CAPS. – Mike Cheel Apr 28 '22 at 22:20
  • 1
    @MikeCheel "*folks*" refers to me in this particular instance since I unilaterally closed the question. I read your question. Again: the first link should explain its use-case. As passer-by pointed out, it is an additional (independent) generic parameter. – Turing85 Apr 28 '22 at 22:21
  • @Turing85 what does java refer to that as? I'm not seeing a clear answer in that first link you referred to. And as mentioned we don't use it in C#. What is its purpose? When do I need it? – Mike Cheel Apr 28 '22 at 22:24
  • 1
    @Turing85 - I don't think the question is a duplicate. The OP is clearly questioning the appearance of the symbol T in two different contexts, and the answer needs to explain that. Your 'duplicate' links do not. FWIW, I have a short program to demonstrate, but can't post it in a comment without it getting mangled. – passer-by Apr 28 '22 at 22:29
  • @MikeCheel It is just the same as a generic parameter on a class, but scoped for the method. You could imagine something like: `public T transformIntToT(Function transformer) { return transformer.apply(someInt); }` (assume that the surrounding class has a field `int someInt`). So the return-type depends on the method parameter, which depends on the generic parameter. – Turing85 Apr 28 '22 at 22:29
  • 1
    Of course. You know that, I know that, but the OP did not. Which is why he asked the question. Pointing to a couple of answers that are only related by the word "generic" does not help anyone. – passer-by Apr 28 '22 at 22:31
  • @Turing85 so basically if I don't want to introduce T to the whole class but instead to just the method, then the additional is necessary? Is this what I am gleaning? – Mike Cheel Apr 28 '22 at 22:33
  • @passer-by thank you for understanding what I am asking. – Mike Cheel Apr 28 '22 at 22:34
  • @MikeCheel Yes. It's just a matter of scoping. If we need a generic parameter for just one method, and the generic parameter can be inferred from the method itself (e.g. through the parameters), there's no need to make the whole class generic. – Turing85 Apr 28 '22 at 22:35
  • @Turing85 are you concurring with my assessment or am I still missing something? – Mike Cheel Apr 28 '22 at 22:36
  • @Turing85 when I do not introduce at the class level and try to define the same method like in my first example of my post, java doesn't like that. If I add the additional like T DoSomething(T value) then it is fine with it. – Mike Cheel Apr 28 '22 at 22:37
  • @MikeCheel The compiler complains in the 1st case since `T` is not a type and no generic parameter is defined. As with variables and fields, generic parameters have to be defined before they can be used. But this is a different question. – Turing85 Apr 28 '22 at 22:43
  • @Turing85 In both my examples in my post, java doesn't care; they both compile. If I remove the from class Test, then it does complain unless I add it down at the method level. So just to confirm I understand correctly, if I want to introduce this type parameter at the method level ONLY, I need that additonal to introduce the type to the method. If I am introducing it to the whole class by doing class Test, then it isn't necessary at the method level. This is what I am seeing but want to confirm that I am not missing anything. – Mike Cheel Apr 28 '22 at 22:49
  • 2
    Again: if we want to use it, we have to declare it first. In the 1st example, if we remove `` from the class, there is no `T` defined. In the 2nd example we "hide" the `` on class-level through the `` on method level (just like a local variable hides a field variable of an object). – Turing85 Apr 28 '22 at 22:51

2 Answers2

3

Maybe this will clear it up. The notation <T> declares a type variable.

So we have one variable T at the class level, and a redeclaration of that same symbol for a particular method.

class Test<T> {
    <T> T doSomething(T value) {
        // <T> declares a new type variable for this one method
        System.out.println("Type of value: " + value.getClass().getSimpleName());
        return value;
    }
    T doSomethingElse(T value) {
        // T is not redeclared here, thus is the type from the class declaration
        System.out.println("Type of value: " + value.getClass().getSimpleName());
        return value;
    }
    public static void main(String... a) {
        Test<String> t = new Test<>();
        t.doSomething(42);
        t.doSomething("foo"); // also works
        t.doSomething(t); // contrived, but still works
        t.doSomethingElse("hi");
        t.doSomethingElse(42); // errors because the type `T` is bound to `String` by the declaration `Test<String> t`
    }
}

In main, I create a Test<String> so the class-level T is String. This applies to my method doSomethingElse.

But for doSomething, T is redeclared. If I call the method with an Integer arg, then T for that case is Integer.

Really, it would have been better to call the second type variable anything else at all, on the declaration of doSomething. U, for example.

(In most cases, I actually favour giving useful names to type variables, not just single letters).

Code-Apprentice
  • 81,660
  • 23
  • 145
  • 268
passer-by
  • 1,103
  • 2
  • 4
  • so in your doSomething above, you could type constrain it and it wouldn't effect other methods that just had T? – Mike Cheel Apr 28 '22 at 22:57
  • Right, The `` is what makes the difference, it introduces a new variable. With a confusing name. – passer-by Apr 28 '22 at 22:59
  • 1
    thank you, c# doesn't have this convention and while I have been programming c# for over 10 years (since 2010 I think, programmed VB.NET before that), I have only been doing java for a couple of days. – Mike Cheel Apr 28 '22 at 23:02
  • @passer-by It might help to also show `t.doSomething("foo"); // works` and `t.doSomethingElse(42); // error`. – Code-Apprentice Apr 28 '22 at 23:03
  • @Code-Apprentice why does doSomethingElse(42) cause an error and not "foo" if there is not a type contraint on T? – Mike Cheel Apr 28 '22 at 23:06
  • @MikeCheel Because the `T` for `doSomethingElse()` is constrained by the type given to the class: `Test`. But the `T` for `doSomething()` is "local" and determined by the parameter type. – Code-Apprentice Apr 28 '22 at 23:07
  • @MikeCheel See https://replit.com/@codeguru/DarkturquoiseUnsungApplicationstack for a running version of this example. – Code-Apprentice Apr 28 '22 at 23:11
  • 1
    @Code-Apprentice thanks, I get it, the class was instantiate with Test, that is why. I've been working over 12 hours today so a little slow. – Mike Cheel Apr 28 '22 at 23:28
  • @CodeApprentice - i agree with your point. If you'd care to edit, go ahead. – passer-by Apr 29 '22 at 00:28
  • @passer-by Done. I thought about editing instead of posting a comment, but decided against it. – Code-Apprentice Apr 29 '22 at 16:40
2

The concept is known as a generic method (docs.oracle.com).

In the code presented, we have an especially tricky case of generics since we have two generic parameters with the same name:

  • the <T> on the class-level: public class Test<T>, and
  • the <T> on the method-level: public <T> T DoSomething(T value)

The latter hides the former within the scope of the method DoSomething(...), just like a local variable would hide an instance field with the same name. In general, I would advice against this type of "hiding" since it makes the code harder to read and understand. Thus, for the rest of the discussion we will work with this (slightly modified) version of the code:

public class Test<T> {
    public T doSomethingWithT(T t) {
        return t;
    }

    public <U> U doSomethingWithU(U u) {
        return u;
    }
}

The scope of the class-level generic parameter T is for the whole class, while the scope of the method-level generic parameter U is only for the one method it is delared on. This will lead to the following observation:

// T is bound to type String for the instance testString:
final Test<String> testString = new Test<>();

final String tString = testString.doSomethingWithT("Hello");
System.out.println(tString); // prints "Hello"
// will not compile since 1 is not a String:
// int tInt = testString.doSomethingWithT(1);

// For this one invocation of doSomethingWithU(...), U is bound to 
// type String:
final String uString = testString.doSomethingWithU("World!");
System.out.println(uString); // prints "World!"

// for this one invocation of doSomethingWithU(...), U is bound to
// type Integer:
final int uInt = testString.doSomethingWithU(1);
System.out.println(uInt); // prints "1"

Ideone demo

Notice that, although doSomethingWithU(...) is a generic method, we did not have to specify the generic parameter, the compiler inferred the type for us. While seldom used, we can also explicitly specify the generic parameter for thie method:

final Test<String> testString = new Test<>();
final Number number = testString.<Number>doSomethingWithU(1);
System.out.println(number);

Ideone demo

(In this example, the explicit generic parameter is not necessary, the code works without it aswell, but there are rare cases where this may be useful or even necessary.)


The following is not strictly necessary to understand generic methods, but more of a curiosity one might find in code and is meant to prime the reader that it is bad practice, should not be used and removed when seen.

It should also be mentioned that the JLS allows us to add generic method parameters on method invocations that do not have any generic parameter. Those parameter do not have any effect:

Object o = new Object();

// Method "hashCode()" on "Object" has not generic parameters, one 
// can "add" one to the method invocation, it has no effect on the 
// semantics, however
int hash = o.<String>hashCode();

Ideone demo


A remark on the code: In Java, methods should be written in camelCase instead of CamelCase (DoSomething(...) -> doSomething(...))

Turing85
  • 18,217
  • 7
  • 33
  • 58
  • 1
    As I mentioned earlier (in comments), I have been C# programmer for a long time but Java only a few days. I also have a fellow C# dev from my work who has also been thrust into java and he is following this post as well. We both appreciate the answers a lot. – Mike Cheel Apr 28 '22 at 23:26