Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java Generics - difference in method declaration

Tags:

java

generics

What is the difference between the following two method declarations:

 1. <R> Stream<R> myFunc(Function<? super T, ? extends R> mapper);    
 2. Stream<R> myFunc(Function<? super T, ? extends R> mapper);

For the 2nd declaration to compile, I need to add type parameter to class like this.

public class MyGenericsTest<T, R>

In this case compiler is ensuring that the return type of myFunc is determined at the compile time. The compiler could have known that from the method signature as well. I am confused on why these 2 declarations are treated differently by compiler.

like image 531
pawinder gupta Avatar asked Sep 29 '16 03:09

pawinder gupta


People also ask

How does a generic method differ from a generic type in Java?

Generic methods are methods that introduce their own type parameters. This is similar to declaring a generic type, but the type parameter's scope is limited to the method where it is declared. Static and non-static generic methods are allowed, as well as generic class constructors.

How do you declare a generic method in Java?

When we declare an instance of a generic type, the type argument passed to the type parameter must be a reference type. We cannot use primitive data types like int, char. Test<int> obj = new Test<int>(20);

What is the difference between generic class and generic method?

A generic class or structure can contain nongeneric procedures, and a nongeneric class, structure, or module can contain generic procedures. A generic procedure can use its type parameters in its normal parameter list, in its return type if it has one, and in its procedure code.


2 Answers

By writing <R> Stream<R> myFunc(Function<? super T, ? extends R> mapper) you are telling the compiler that:

  • R is any class and is local to the method (by starting with<R> at the beginning)
  • The return type is a Stream of R
  • T is a class specified in the type parameter of MyGenericsTest<T> (if you dont specify it, it wont work as the compiler will not know T)

If you change to Stream<R> myFunc(Function<? super T, ? extends R> mapper), R and T are not local (no <R, T> at the beginning of the method) and the compiler expects them to be defined at a class level as MyGenericsTest<T, R>

like image 175
ortis Avatar answered Sep 20 '22 11:09

ortis


The second form:

Stream<R> myFunc(Function<? super T, ? extends R> mapper);

really isn't any different from this:

Stream<String> myFunc(Function<? super T, ? extends String> mapper);

to the compiler, except that it uses a different type. The second one returns a stream of String. The first one returns a stream of R, whatever R is. The compiler already knows what String is. For the first one, the compiler has to know what R is, which means it has to be defined somewhere. It can be a generic parameter on an outer class, but it could also be a non-generic class that you import from somewhere else (written by someone who is very bad at coming up with meaningful names).

Keep in mind that although we often use single upper-case letters as generic parameters, that's just a human convention. The compiler just treats it like any other identifier.

But that's why your two examples are so different. The first one is a syntax that defines the method as a generic method with a type parameter that you're calling R. The second one is the exact same syntax as a method that returns Stream<String> or List<Integer> or something.

like image 43
ajb Avatar answered Sep 21 '22 11:09

ajb