Reputation: 1766
I have to write a constructor for a class Stamp. The constructor is supposed to take up to five arguments of types String, int, and double. So, the constructor would look like this:
public Stamp(String code, int year, String country, double value, int numberOfCopies){...}
The problem is, as the object of the class Stamp is being created, not all the parameters may be provided, i.e., an object can be declared as
Stamp stamp = new Stamp("some_code", 1991, "Burkina Faso", 50, 1000);
as well as
Stamp stamp = new Stamp("some_code", 1991, "Burkina Faso");
and the constructor has to work in both cases, even if the list of arguments is truncated (in the latter case, some default values are assigned to value and numberOfCopies). Of course, I can just write six constructors (for the possible number of parameters from 0 to 5, assuming that the parameters always follow the order described above, without mixing up), but there should be a smarter way. I know I can probably declare the constructor like
public Stamp(Object[] params){...}
and then cast the elements of params into corresponding classes. This will probably work but I will always have to check how many parameters are provided for the constructor using "if" conditions in order to decide whether to assign default values to variables if the corresponding parameters are not provided or use the provided values if those are given. This all seems pretty ugly. Thus, the question is brief: what is the way to build a constructor (or some other method) if the list of provided parameters varies in length and the types of parameters are different?
Upvotes: 6
Views: 13225
Reputation: 81578
Indeed, as others stated, a possible solution would be to reuse constructors using this()
calls.
If the order was variable and you wouldn't be able to tell which one is given and which one is not, then the Object[]
would be insufficient. You would need a Map<String, Object>
, and you would need to write explicit conversion for them. There might be a better way of doing this with Reflection too, but I haven't got that completely figured out yet.
So the "simple" approach would be that instead of
public Stamp(String code, int year, String country, double value, int numberOfCopies){...}
You would have
public enum StampProperties
{
CODE("code"),
YEAR("year"),
COUNTRY("country"),
VALUE("value"),
NUMBER_OF_COPIES("numberOfCopies");
private String identifier;
private StampProperties(String identifier)
{
this.identifier = identifier;
}
public boolean c(String id)
{
return identifier.equals(id);
}
}
public Stamp(Map<String, Object> params)
{
for(String string : params.keySet())
{
mapProperty(params, string);
}
}
private void mapProperty(Map<String, Object> params, String identifier)
{
Object object = params.get(identifier);
if(StampProperties.CODE.c(identifier))
{
this.code = (String) object;
}
else if(StampProperties.YEAR.c(identifier))
{
this.year = ((Integer) object).intValue();
}
else if...
}
But I really think the other solutions are a bit better, because they take less code, are inherently type-safe (less likely to mess up), and honestly, I'd go for the builder pattern
stated by Ian Roberts
, I really like that answer.
Upvotes: 0
Reputation: 29
Use method overloading,java support this. Means you can write same method in same class with different parameters,just use copy paste and make multiple methods.:)
Upvotes: -1
Reputation: 122414
This may be a suitable use for a builder pattern
public class Stamp {
public static class Builder {
// default values
private String code = "default code"
private int year = 1900;
// etc.
public Builder withCode(String code) {
this.code = code;
return this;
}
public Builder withYear(int year) {
this.year = year;
return this;
}
// etc.
public Stamp build() {
return new Stamp(code, year, country, value, numberOfCopies);
}
}
public Stamp(String code, int year, String country, double value,
int numberOfCopies){...}
}
The construction process then becomes
Stamp s = new Stamp.Builder()
.withCode("some_code")
.withYear(1991)
.build();
This way the arguments are no longer order-dependent - you could equivalently say
Stamp s = new Stamp.Builder()
.withYear(1991)
.withCode("some_code")
.build();
Upvotes: 13
Reputation: 11483
If the items themselves being passed didn't matter, you could use varargs:
public Stamp(Object... obj) {
}
However, given your example, it sounds like you want to add constructor overloading. Since all the data seems to have meaning and represent a specific thing, I would simply overload like so:
public Stamp(String code, int year) {
this(code, year, "Burkina Faso");
}
public Stamp(String code, int year, String location) {
//...
}
Upvotes: 0
Reputation: 122026
Instead of messing up with Object[]
go with constructor reuse.
Provide all possible constructors and use them internally
Just an ex;
public Stamp(String code, int year)
{
this(code, "", year,0,0); //calling your main constructor with missed values.
}
No other work around as of now.
Upvotes: 5