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
bto be aStringwhich is what we provide.Yes. The method expects
bto be anIntegerwhich is what we provide.No. The method expects
bto be a aStringbut we provide aDouble.Yes. The method expects a
Shapeand we provide aCircle. Those are compatible.No. The
Shapeclass cannot be instantiated. Also, the method expects aCircleand we attempt to give it aShape. Those types are not compatible.No. The method expects a
Circlebut 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
bto be aStringand returns aString. Everything is compatible.No. The method expects
bto be aStringbut we provide a floating point number.Yes. The method expects
bto be aShapeand we provide a compatibleCircle. Also, the return type isShapeand we store that value in aShapevariable.No. The method expects a
Shapeand we provide aCircle. Those are compatible. However, the method returns a reference to aShapewhich cannot be assigned to aCirclevariable (remember, not all shapes are circles).Yes. The method expects a
Rectangleand 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
intA 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
foomethod 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");