- Functional Default Arguments, Part One
- Outline
- Functional Default Arguments
- Background
- Method Overloading
- Varargs
- Null values
- Other Approaches: Maps, Optional, and the Builder Pattern
- Sign Up for the Newsletter
- Does Java have default parameters?
- 1. Java default parameters in practice
- 1.1. Java default parameters with method overloading
- 1.2. Allowing Nulls as method parameters
- 1.3. Varargs parameter
- 2. Java default parameters in long parameter list
- 2.1. Solving Java default parameters with Parameter Object
- 2.2. … and Builder patterns
- Conclusion
- how to set default method argument values? [duplicate]
- 7 Answers 7
Functional Default Arguments, Part One
Join the DZone community and get the full member experience.
Outline
Java lacks a built-in way to define default arguments for methods and constructors. Over the years, several approaches have been proposed, each with its pros and cons. The most widely-known one uses method overloading, though varargs, null values, the builder pattern, and even maps have been used as well. Here we propose a new approach based on functional constructs.
Functional Default Arguments
Background
Many languages support default arguments for methods and constructors out of the box, i.e. Scala:
def sum(x: Int = 6, y: Int = 7): Int = x + y
The sum method can be invoked as follows:
sum(1, 2) // 3 -> x = 1, y = 2 (no defaults) sum(3) // 10 -> x = 3, default y = 7 sum(y = 5) // 11 -> default x = 6, y = 5 sum() // 13 -> default x = 6, default y = 7 sum(y = 3, x = 4) // 7 -> x = 4, y = 3 (no defaults)
This is very handy, but Java doesn’t support it. There are a few different ways to accomplish something similar, however all of them have some drawback.
Method Overloading
The most widely-known one uses method overloading to simulate default arguments:
public int sum(int x, int y) < return x + y; // actual implementation >public int sum(int x) < // default y = 7 return this.sum(x, 7); >public int sum() < // default x = 6 return this.sum(6); // and y = 7 (implicitly) >
Despite this is a very common pattern (or anti-pattern) in Java, it has some drawbacks:
- The number of overloads for the method increases exponentially with the number of arguments, since all possible, meaningful argument combinations must be considered.
- Some argument combinations are not possible because overloaded methods are differentiated by the number and the type of the arguments passed into the method. In the example above, there’s no way to define an overload sum(int y) that defaults the value of x, because we have chosen to specify x as an explicit argument (defaulting y to 7).
- Definition of default argument values is implemented inside the overloaded methods, thus making defaults global to every caller. In other words, this does not take the method invocation’s context into account.
Varargs
Another approach to default arguments in Java is using varargs:
public int sum(int. arguments) < // Define default values int x = 6; int y = 7; // Extract explicit argument values, checking bounds if (arguments != null && arguments.length >= 1) < x = arguments[0]; if (arguments.length >= 2) < y = arguments[1]; >> return x + y; >
Here we define the default values and then extract the explicit arguments from the varargs parameter. Each default value is preserved only if its corresponding explicit value is not present in the varargs parameter.
Drawbacks of this approach are:
- As per the Java Language Specification, varargs parameters must be specified at the end of the argument list.
- If default arguments were of different types, the varargs parameter definition should be changed to Object. arguments. But if we do this, we lose static type checking, so we would have to check each argument’s type at runtime, cast it and handle all possible errors.
- Both definition of default argument values and handling of the varargs parameter are to be implemented inside the method. As with the method overloading approach, defaults remain global to every caller, so the method invocation’s context is not taken into account.
Null values
This approach is quite simple: if you invoke the method with a null argument, then its corresponding default value is used instead:
public int sum(Integer x, Integer y) < // Define default values x = x == null ? 6 : x; y = y == null ? 7 : y; return x + y; >
Using null values is much simpler than previous approaches. However it still has some drawbacks:
- As null is to be used to specify a default value, it cannot be used as an argument’s valid explicit value.
- Primitives are not allowed, since only references can be null. This is why we’ve used wrapper types in the example above.
- Checking for null arguments and assigning default values has to be implemented inside the method. Again, defaults remain global to every caller, so the method invocation’s context is not taken into account.
Other Approaches: Maps, Optional, and the Builder Pattern
In this StackOverflow answer given by user Vitalii Fedorenko, all common approaches to default arguments are visited. I won’t analyze them here, since there are already a lot of articles that explore their pros and cons. Instead, I would like to introduce a new way to work with default arguments in Java that takes functional programming into consideration. But that will have to wait until the second part of this article.
Sign Up for the Newsletter
Did you enjoy this post? If so, stay tuned for part two tomorrow, and please consider signing up to The Bounds of Java newsletter. I usually write a new post every 2-4 weeks, so if you’d like to be kept in the loop for the next one, I’d really appreciate it if you’d sign up.
Java (programming language) Anti-pattern Builder pattern Java language Functional programming Cons POST (HTTP) Scala (programming language) Extract
Published at DZone with permission of Federico Peralta Schaffner , DZone MVB . See the original article here.
Opinions expressed by DZone contributors are their own.
Does Java have default parameters?
Many programming languages like C++ or modern JavaScript have a simple option to call a function without providing values for its arguments. In Java, default method parameters require a bit more typing to achieve this effect in comparison to other languages. From this article, you will learn how the default method parameters work in Java.
1. Java default parameters in practice
The syntax of Java language doesn’t allow you to declare a method with a predefined value for a parameter. Fortunately, you can achieve the same effect with simple code constructions.
There are several options that you may use to simulate behavior known from other programming languages. You can:
- use method overloading
- allow nulls as an input
- declare a method with Java Varargs
Let’s take a closer look at these options.
1.1. Java default parameters with method overloading
Probably the best option to achieve default method parameters in Java is by using the method overloading. Method overloading allows you to declare several methods with the same name but with a different number of parameters.
How to use method overloading to simulate default method parameters?
By calling a more complex method by a simpler one.
Here is a practical example of a method which allows you to query for an optional number of some documents:
List search(String query) < return search(query, 10, 0); >List search(String query, Integer limit, Integer offset) < // method implementation >
The only required parameter is a query string. As you can see, you only have to implement the method with the highest number of parameters. To clarify, the simple method is just a proxy that passes its parameter to the more complex method and provides default parameter values for the optional arguments.
In comparison to other options, the main advantage of method overloading is the simplicity for the client code. You can easily read what set of parameters is allowed for a given method.
Although method overloading is strongly recommended, it’s worth knowing about other possibilities.
1.2. Allowing Nulls as method parameters
Another option is to declare a single method that accepts all possible parameters. Next, in the body of the method, you can check which parameters are nulls and assign them default values.
In this approach, our previous example looks as follows:
List search(String query, Integer limit, Integer offset) < if (limit == null) < limit = 10; >if (offset == null) < offset = 0; >// method implementation >
There are two main disadvantages to this approach. First, you still need to pass all arguments in client code. Even if some of them are nulls. Second, you need to know which parameters are nullable as you can’t tell that just by looking at the method declaration.
What is more, many developers consider reassinging method parameters as bad practice as it makes the code harder to follow. With this in mind, many static code analysis tools allow you to check for this kind of code smell.
1.3. Varargs parameter
The Varargs option is limited only to arguments of the same type and meaning. Therefore, it doesn’t actually solve the problem of default parameters in Java methods. But you may consider it in some cases.
User createUser(String login, Right. rights) < rights = User.DEFAULT_RIGHTS; // implementation >
Technically, it’s even possible to use Varargs as a single optional parameter. In this case, you don’t have to pass null in the client code to use the default value. Yet, it rather feels like an awful hack. I recommend sticking to method overloading.
2. Java default parameters in long parameter list
The main problem with method overloading as a solution for default parameter values reveals itself when a method accepts multiple parameters. Creating an overloaded method for each possible combination of parameters might be cumbersome.
Simply put, the Parameter Object is a wrapper object for all parameters of a method.
But wait! Aren’t we just moving the problem of the long parameter list from a method to the constructor of the new class?
Indead, we are. But class constructors give us one additional solution for the problem which is the Builder pattern.
2.1. Solving Java default parameters with Parameter Object
As mentioned before, the first thing you need is a parameter object class that wraps the list of method parameters. Let’s see how it can look like for a method from the previous example:
As you can see, the implemented parameter object is nothing more than just a regular POJO. The advantage of the Parameter Object over a regular method parameter list is the fact that class fields can have default values. Just like in the above example.
That’s it. Now you have all default parameters in a single place.
2.2. … and Builder patterns
Once you create a wrapper class for the method parameter list you should also create a corresponding builder class. Usually, you’ll do it as an inner static class.
But don’t type builders alone, automate this work!
Popular Java IDEs provide plugins that generate builder classes.
In our example the builder will look as follows:
class SearchParams < // getters . private SearchParams(Builder builder) < query = builder.query; limit = builder.limit; offset = builder.offset; >public static Builder newBuilder() < return new Builder(); >public static final class Builder < private String query; private int limit; private int offset; private Builder() < >public Builder withQuery(String val) < query = val; return this; >public Builder withLimit(int val) < limit = val; return this; >public Builder withOffset(int val) < offset = val; return this; >public SearchParams build() < return new SearchParams(this); >> >
The final step is to use the builder to construct a new parameter object. In this case, you assign only selected parameters. For those parameters you skip, their default values are going to be used. Here’s an example:
SearchParams params = SearchParams.newBuilder() .withQuery("gold") .withOffset(20) .build(); // params.limit = 10 by default
Our example is really simple as the method has only three parameters so it might look like overengineering. In fact, the solution for default parameters using Parameter Object and Builder shines when the list of method parameters is much longer.
Conclusion
Java doesn’t have a simple solution for default method parameters as available in other common programming languages. Yet, you can simulate it using other Java constructions and patterns. In addition, you learn a simple approach with method overloading which works for methods with a short parameter list. For complex methods, the Parameter Object pattern is more flexible.
If you find the article useful, please share it with your friends. Don’t forget to subscribe so I can notify you about other similar posts.
how to set default method argument values? [duplicate]
is it possible to modify the given method in order to be able to call it with and without parameters? example:
doSomething(param1, param2); doSomething();
Basically the same as this one: ‘Does Java support default parameter values?’, stackoverflow.com/questions/997482/…
7 Answers 7
You can accomplish this via method overloading.
public int doSomething(int arg1, int arg2) < return 0; >public int doSomething()
By creating this parameterless method you are allowing the user to call the parameterfull method with the default arguments you supply within the implementation of the parameterless method. This is known as overloading the method.
If your arguments are the same type you could use varargs:
public int something(int. args) < int a = 0; int b = 0; if (args.length >0) < a = args[0]; >if (args.length > 1) < b = args[1]; >return a + b >
but this way you lose the semantics of the individual arguments, or
have a method overloaded which relays the call to the parametered version
or if the method is part of some kind of initialization procedure, you could use the builder pattern instead:
class FoodBuilder < int saltAmount; int meatAmount; FoodBuilder setSaltAmount(int saltAmount) < this.saltAmount = saltAmount; return this; >FoodBuilder setMeatAmount(int meatAmount) < this.meatAmount = meatAmount; return this; >Food build() < return new Food(saltAmount, meatAmount); >> Food f = new FoodBuilder().setSaltAmount(10).build(); Food f2 = new FoodBuilder().setSaltAmount(10).setMeatAmount(5).build();
Then work with the Food object
The builder pattern allows you to add/remove parameters later on and you don’t need to create new overloaded methods for them.