10.6. Generic Methods¶
The general layout for a generic method in Java is below. Note: Anything
enclosed in [ ]
is optional. Also, omitting the visibility modifier
will make the method package-private.
[visibility] [static] <PlaceholderType> ReturnType methodName([params])
<PlaceholderType>
denotes the type parameter(s), i.e., the reference types that are replaced when the method is invoked. Regarding type parameters (placeholders):Enclosed in angle brackets:
<
,>
.Multiple placeholders are allowed. In that scenario, the types should be comma-separated, e.g.,
<T, U>
.The placeholder type can, but is not required to be, the return type of the method.
The placeholder type can, but is not required to be, the type for any of the method’s parameters.
The placeholder type can, but is not required to be, the type for any of the method’s local variables.
In the examples below, don’t focus on the specifics of what each method does. Instead, focus on the data types being used and how they are compatible with the given type parameters, return types, and formal parameters.
10.6.1. Example with generic return-type only¶
public static <T> T foo(int a, int b)
When we call foo
, we can replace T
with any reference type. For
example, if we assume foo
is defined in a class called Utility
,
the following calls would be valid:
1Drone d = Utility.<Drone>foo(7, 4);
2Camera c = Utility.<Camera>foo(18, 24);
On line 1, for example, the return type will be Drone
since we passed in
Drone
as our type parameter (to replace T
).
10.6.2. Example with a generic formal parameter¶
public static <T> int foo(String a, T b)
We will again assume that the foo
method is in the Utility
class. Now,
when we specify the type parameter, it will impact the method’s expectations
about the datatype of b
. If the type of b
and the type parameter
are not compatible, the code will not compile (which is better than it crashing
at runtime).
Drone d = new Drone(147.5);
// The next line compiles and runs
Utility.<Drone>foo("hello", d);
// Does not compile. foo expects ``b`` to be of type Camera
Utility.<Camera>foo("hello", d);
Note
When trying to figure out if a particular line of code will compile, it sometimes
helps to rewrite the method signature and plug in the value of the type parameter
in place of the type argument. Taking the two calls to foo
above as examples,
the first has a type parameter of Drone
. If we replace the type argument T
in the original method signature, we see that the new signature would be:
public static int foo(String a, Drone b)
This is how Java is viewing the method for that particular call. When our driver
program calls the method with Utility.<Drone>foo("hello", d)
, we can now
see that the method is expecting a Drone
as the second parameter which matches
what we provided.
However, in the second call to foo
above, the type parameter is Camera
, so
the method signature becomes:
public static int foo(String a, Camera b)
Then, we call it using: Utility.<Camera>foo("hello", d);
which passes a
Drone
as the second parameter (when foo
expects a Camera
). These are
incompatible and will therefore not compile!
Test Yourself
Will the following lines compile? Asuume that Shape
is an
abstract parent class for both Ellipse
and Rectangle
and that Ellipse
is a parent of Circle
and
Rectangle
is a parent of Square
. You should also assume
we have access to all of those classes.
1Utility.<String>foo("hello", "world");
2Utility.<Integer>foo("hello", 7);
3Utility.<String>foo("hello", 7.4);
4Utility.<Shape>foo("hello", new Circle(2.4));
5Utility.<Circle>foo("hello", new Shape());
6Utility.<Circle>foo("hello", new Rectangle(3.89));
Test Yourself Solutions (open after attempting the questions above)
Here are the answers for each line in the code above:
Yes. The method expects
b
to be aString
which is what we provide.Yes. The method expects
b
to be anInteger
which is what we provide.No. The method expects
b
to be a aString
but we provide aDouble
.Yes. The method expects a
Shape
and we provide aCircle
. Those are compatible.No. The
Shape
class cannot be instantiated. Also, the method expects aCircle
and we attempt to give it aShape
. Those types are not compatible.No. The method expects a
Circle
but we provide aRectangle
. Those are not compatible types.
10.6.3. Example with a generic return-type and generic parameter¶
public static <T> T foo(String a, T b)
We will again assume that the foo
method is in the Utility
class. Now,
when we specify the type parameter, it will impact the method’s expectations
about the datatype of b
and also serve as the return type of the method.
// The next line compiles and runs
Drone d = new Drone(147.5);
Drone d2 = Utility.<Drone>foo("hello", d);
// Does not compile. foo expects b to be of type Camera
Camera c = Utility.<Camera>foo("hello", d);
Test Yourself
Will the following lines compile? Asuume that Shape
is an
abstract parent class for both Ellipse
and Rectangle
and that Ellipse
is a parent of Circle
and
Rectangle
is a parent of Square
. You should also assume
we have access to all of those classes.
1String s = Utility.<String>foo("hello", "world");
2String s2 = Utility.<String>foo("hello", 7.4);
3
4Shape sh = Utility.<Shape>foo("hello", new Circle(4.29));
5Circle c = Utility.<Shape>foo("hello", new Circle(2.4));
6Rectangle r = Utility.<Rectangle>foo("hello", new Square(3.4));
Test Yourself Solutions (open after attempting the questions above)
Here are the answers for each line in the code above:
Yes. The method expects
b
to be aString
and returns aString
. Everything is compatible.No. The method expects
b
to be aString
but we provide a floating point number.Yes. The method expects
b
to be aShape
and we provide a compatibleCircle
. Also, the return type isShape
and we store that value in aShape
variable.No. The method expects a
Shape
and we provide aCircle
. Those are compatible. However, the method returns a reference to aShape
which cannot be assigned to aCircle
variable (remember, not all shapes are circles).Yes. The method expects a
Rectangle
and we provide aSquare
. Those are compatible. The return type also matches the type returned.
10.6.4. Example of a non-static generic method¶
public <T> T foo(String a, T b)
If we still assume that the foo
method is in the Utility
class, we
now need an object of type Utility
to call the method. The other
aspects of the method remain the same. A valid call would look like this:
Utility util = new Utility();
String s = util.<String>foo("Hello", "World");
The examples where foo
was static hold here as well. The only change
is in how the method is called.
10.6.5. Example with a non-static generic method in a generic class¶
public class SomeClass<T> {
public <R> T foo(T a, R b) {
...
} // foo
} // someClass
This is likely the most complicated combination of generic classes and
generic methods you would see in this course. In the example above,
the class has its own placeholder type T
, which can be used
throughout the class in any non-static method. The foo
method is
non-static and has its own placeholder type R
. You might see
something like this:
SomeClass<Integer> sc = new SomeClass<Integer>();
Integer int1 = sc.<String>foo(12, "help");
Integer int2 = sc.foo(12, "help"); // R = String
Rapid Fire Review
Which symbol is used to denote the type parameter(s) in a generic method?
Parentheses
()
Square brackets
[]
Curly braces
{}
Angle brackets
<>
Where is the type parameter declared in a generic method?
After the method name
Before the method return type
Inside the method body
After the method parameters
What happens if the type of a parameter is incompatible with the type argument in a generic method?
The program will crash at runtime.
The program will run with a warning.
The program will fail to compile.
Both (a) and (b)
In the following method signature, what is
T
?public static <T> T foo(int a, int b)
A placeholder for a reference type
A return type fixed to
int
A parameter name for a formal parameter
A local variable declared in the method
Review - Compatibility Questions
For this set of questions, you can safely assume that foo
is in the Utility
class and that you have access to the
reference types used in the code.
Which of the following calls to the
foo
method will successfully compile given this method signature?public static <T> T foo(int a, T b)
Camera c = Utility.<Camera>foo(7, new Drone(175.0));
Integer i = Utility.<Integer>foo(7, "hello");
Drone d = Utility.<Drone>foo(7, new Drone(175.0));
Shape s = Utility.<Shape>foo(7, "hello");
Which of the following calls to the foo method will successfully compile given this method signature?
public static <T> T foo(T a, T b)
Camera c = Utility.<Camera>foo(new Camera(128.0), new Drone(175.0));
String s = Utility.<String>foo("hello", "world");
Integer i = Utility.<Integer>foo("hello", 7);
Circle c = Utility.<Shape>foo("hello", new Circle(2.4));
Which of the following calls to the foo method will successfully compile given this method signature?
public <T> int foo(String a, T b)
int x = util.<Drone>foo("Amazeon", new Drone(175.0));
Circle c = util.<Circle>foo("hello", new Rectangle(4.0));
Integer i = util.<Integer>foo("hi", 5.6);
Camera z = util.<Camera>foo("world", "camera");