Java Method Parameters: Varargs, Overloading, and Recursion
Variable Arguments (Varargs)
Since JDK 1.5, when defining a method, if the type of parameters is determined but the number of parameters is uncertain, you can use variable arguments. The syntax for varargs is:
[modifier] returnType methodName([non-vararg parameter list,] parameterType... parameterName) { }
Characteristics and rules of varargs:
- A method can have at most one varargs parameter.
- If a method contains a varargs parameter, it must be the last in the parameter list.
- Inside the method, the varargs parameter is used as an array.
- The syntax is essentially equivalent to:
However, the first form is more flexible because you can pass an array or individual elements driectly.[modifier] returnType methodName([non-vararg parameter list,] parameterType[] parameterName) { }
1. Method with Only Varargs
Example: Calculate the sum of n integers.
public class NumberTools {
// Method using array parameter (must pass array)
int total(int[] nums) {
int sum = 0;
for (int i = 0; i < nums.length; i++) {
sum += nums[i];
}
return sum;
}
// Method using varargs (can pass array or individual elements)
int sum(int... nums) {
int sum = 0;
for (int i = 0; i < nums.length; i++) {
sum += nums[i];
}
return sum;
}
}
public class TestVarParam {
public static void main(String[] args) {
NumberTools tools = new NumberTools();
// Using varargs method
System.out.println(tools.sum()); // 0 arguments
System.out.println(tools.sum(5)); // 1 argument
System.out.println(tools.sum(5, 6, 2, 4)); // 4 arguments
System.out.println(tools.sum(new int[]{5, 6, 2, 4})); // array argument
System.out.println("------------------------------------");
// Using array method
System.out.println(tools.total(new int[]{})); // empty array
System.out.println(tools.total(new int[]{5})); // single element
System.out.println(tools.total(new int[]{5, 6, 2, 4})); // array
}
}
2. Method with Both Non-Varargs and Varargs
- The non-varargs part must receive arguments of the corresponding type and count.
- The varargs part can receive 0 to n arguments of the specified type or an array of that type.
Example: Concatenate strings with a separator.
public class StringTools {
String concat(char separator, String... args) {
String result = "";
for (int i = 0; i < args.length; i++) {
if (i == 0) {
result += args[i];
} else {
result += separator + args[i];
}
}
return result;
}
}
public class StringToolsTest {
public static void main(String[] args) {
StringTools tools = new StringTools();
System.out.println(tools.concat('-')); // ""
System.out.println(tools.concat('-', "hello")); // "hello"
System.out.println(tools.concat('-', "hello", "world")); // "hello-world"
System.out.println(tools.concat('-', "hello", "world", "java")); // "hello-world-java"
}
}
Command-Line Arguments (Optional)
Arguments passed via the command line to the main method's String[] args are called command-line arguments.
public class TestCommandParam {
public static void main(String[] args) {
System.out.println(args);
System.out.println(args.length);
for (int i = 0; i < args.length; i++) {
System.out.println("Argument " + (i + 1) + " value: " + args[i]);
}
}
}
Command-line execution:
java TestCommandParam
java TestCommandParam 1 2 3
java TestCommandParam hello world
Parameter Passing Mechanism
How are arguments passsed to parameters? Does the modification of a parameter affect the argument?
- When a parameter is of a primitive type, changing the parameter's value does not affect the original argument.
- When a parameter is of a reference type, changing the parameter's reference (e.g., assigning a new object) does not affect the original argument, but modifying the object's internal data (e.g., array elements or object fields) does affect the original object.
- Note: Special types like
StringandIntegermay behave unexpectedly.
- Note: Special types like
1. Primitive Type Parameter
Example: Swap two integer values.
public class PrimitiveTypeParam {
void swap(int a, int b) {
int temp = a;
a = b;
b = temp;
}
public static void main(String[] args) {
PrimitiveTypeParam tools = new PrimitiveTypeParam();
int x = 1, y = 2;
System.out.println("Before swap: x = " + x + ", y = " + y); // 1,2
tools.swap(x, y);
System.out.println("After swap: x = " + x + ", y = " + y); // 1,2 (unchanged)
}
}
2. Array Type Parameter
public class ArrayTypeParam {
// Sorts the array (modifies the original)
void sort(int[] arr) {
for (int i = 1; i < arr.length - 1; i++) {
for (int j = 0; j < arr.length - i - 1; j++) {
if (arr[j] > arr[j + 1]) {
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
}
// Prints array elements (does not modify)
void iterate(int[] arr) {
for (int i = 0; i < arr.length; i++) {
System.out.print(arr[i] + " ");
}
System.out.println();
}
public static void main(String[] args) {
ArrayTypeParam tools = new ArrayTypeParam();
int[] nums = {4, 3, 1, 6, 7};
System.out.println("Before sorting:");
tools.iterate(nums);
tools.sort(nums); // modifies nums
System.out.println("After sorting:");
tools.iterate(nums);
}
}
3. Reference Type Parameter
public class ReferenceTypeParam {
void swap(MyData my) {
int temp = my.x;
my.x = my.y;
my.y = temp;
}
public static void main(String[] args) {
ReferenceTypeParam tools = new ReferenceTypeParam();
MyData data = new MyData();
data.x = 1;
data.y = 2;
System.out.println("Before swap: x = " + data.x + ", y = " + data.y); // 1,2
tools.swap(data);
System.out.println("After swap: x = " + data.x + ", y = " + data.y); // 2,1 (changed)
}
}
class MyData {
int x;
int y;
}
4. Parameter Assigned to New Object
public class AssignNewObjectToFormalParam {
void swap(MyData my) {
my = new MyData(); // now my points to a new object
int temp = my.x;
my.x = my.y;
my.y = temp;
}
public static void main(String[] args) {
AssignNewObjectToFormalParam tools = new AssignNewObjectToFormalParam();
MyData data = new MyData();
data.x = 1;
data.y = 2;
System.out.println("Before swap: x = " + data.x + ", y = " + data.y); // 1,2
tools.swap(data);
System.out.println("After swap: x = " + data.x + ", y = " + data.y); // 1,2 (unchanged)
}
}
Method Overloading
Method overloading allows multiple methods with the same name but diffferent parameter lists in the same class. It does not depend on the modifier or return type.
- Parameter list differences: different number of parameters, different types, or different order (rare and not recommended).
- The JVM selects the appropriate method based on the argument list.
- First, look for an exact match.
- Then, look for compatible matches. If multiple compatible methods exist, ambiguity occurs.
Example: Overload methods to find the maximum value.
public class MathTools {
// Maximum of two integers
public int max(int a, int b) {
return a > b ? a : b;
}
// Maximum of two doubles
public double max(double a, double b) {
return a > b ? a : b;
}
// Maximum of three integers
public int max(int a, int b, int c) {
return max(max(a, b), c);
}
// Maximum of n integers
public int max(int... nums) {
int max = nums[0]; // assumes at least one argument
for (int i = 1; i < nums.length; i++) {
if (nums[i] > max) {
max = nums[i];
}
}
return max;
}
}
1. Exact Match
public class MethodOverloadMostMatch {
public static void main(String[] args) {
MathTools tools = new MathTools();
System.out.println(tools.max(5, 3)); // int, int
System.out.println(tools.max(5, 3, 8)); // int, int, int
System.out.println(tools.max(5.7, 2.5)); // double, double
}
}
2. Compatible Match
public class MethodOverloadMostCompatible {
public static void main(String[] args) {
MathTools tools = new MathTools();
System.out.println(tools.max(5.7, 9)); // double, int -> compatible with double, double
System.out.println(tools.max(5, 6, 8, 3)); // varargs
// System.out.println(tools.max(5.7, 9.2, 6.9)); // no compatible method (three doubles)
}
}
Recursive Method Calls
Recursion occurs when a method calls itself. There are two types: direct recursion (A calls A) and indirect recursion (A calls B, B calls C, C calls A).
Important:
- Recursion must have a base condition to terminate; otherwise, a stack overflow occurs.
- Even with a base condition, deep recursion can cause inefficiency or stack overflow. Use loops instead when possible.
Example: Calculate the nth Fibonacci number. Fibonacci sequence: 1, 1, 2, 3, 5, 8, 13, 21, 34, ... Formula: f(n) = f(n-2) + f(n-1), with f(1) = f(2) = 1.
public class FibonacciTest {
public static void main(String[] args) {
FibonacciTest t = new FibonacciTest();
for (int i = 1; i <= 10; i++) {
System.out.println("Fibonacci number " + i + ": " + t.f(i));
}
System.out.println(t.f(20)); // 6765
}
int f(int n) {
if (n <= 1) return 1; // handle n < 1
if (n == 1 || n == 2) return 1;
return f(n - 2) + f(n - 1);
}
}
Object Arrays
An object array stores references to objects. The array itself must be created first (specifying length), then each element object must be created individually; otherwise, default elements are null, causing a NullPointerException.
Example: Define a Rectangle class, then create an array of Rectangle objects and display their info.
public class Rectangle {
double length;
double width;
double area() {
return length * width;
}
double perimeter() {
return 2 * (length + width);
}
String getInfo() {
return "Length: " + length +
", Width: " + width +
", Area: " + area() +
", Perimeter: " + perimeter();
}
}
public class ObjectArrayTest {
public static void main(String[] args) {
// Declare and create a Rectangle array with 3 elements
Rectangle[] array = new Rectangle[3];
for (int i = 0; i < array.length; i++) {
array[i] = new Rectangle();
array[i].length = (i + 1) * 10;
array[i].width = (2 * i + 1) * 5;
System.out.println(array[i].getInfo());
}
}
}