0

What is the quickest/most elegant way to assign values that were passed through a constructor?

For example, if I have the class below:

public MyClass {

    private String firstName;
    private String middleName;
    private String lastName;
    private int age;
    private int birthDay;
    private int birthMonth;
    private int birthYear;

    public MyClass(String firstName, String middleName, String lastName, 
                   int age, int birthDay, int birthMonth, int birthYear) {
        this.firstName = firstName;
        this.middleName = middleName;
        this.lastName = lastName;
        this.age = age;
        this.birthDay = birthDay;
        this.birthMonth = birthMonth;
        this.birthYear = birthYear;
    }

}

Is this really the best way to assign all the values to the class? It seems very bloated and redundant.

Is there any other method available to cut down the redundancy?

Gary Holiday
  • 3,297
  • 3
  • 31
  • 72
  • Seems perfect to me, if you are worried about it being bloated maybe think more about the architecture of your program, i.e can your Class be split up a little into more meaningful Classes. – Luke Garrigan Dec 09 '17 at 22:06
  • 1
    Yes (well, you could also use a Builder or a Factory pattern); but Java **does not** have *Auto Implemented Properties*. – Elliott Frisch Dec 09 '17 at 22:07
  • So there is no way in Java to take the values passed in through the constructor and assign them in sequence to the class's variables? – Gary Holiday Dec 09 '17 at 22:08
  • Where do you see redundancy? – ACV Dec 09 '17 at 22:10
  • 1
    One thing you could do to reduce the number of arguments to this constructor example would be to combine the `age`, `birthDay`, `birthMonth`, and `birthYear`, into a single field `dateOfBirth`. From this your class could then do the math required to calculate other values when needed. –  Dec 09 '17 at 22:10
  • @ElliottFrisch can you please explain how using the Factory pattern is going to decrease the bloated nature – Luke Garrigan Dec 09 '17 at 22:11
  • 1
    @ACV the redundancy is in the repetition of `this.[PARAMETER_NAME] = [PARAMETER_NAME]`. – Jon Kiparsky Dec 09 '17 at 22:12
  • @Fubar By moving "the bloat" from point A to point B; it makes A more aesthetically pleasing. I'm not certain what you're asking me to clarify (I started with "Yes", you need to assign the values). – Elliott Frisch Dec 09 '17 at 22:15

5 Answers5

1

The way you're doing it is just fine. Think of the following:

By initializing/setting variables through a constructor (or really any method) you can do additional validation on the values passed through.

You can e.g. check if the birthMonth parameter is a number between 1 and 12 and throw an IllegalArgumentException if it is not. Or you can check that firstName does actually contain something and is not just "" (an empty string).


There is no cleaner or quicker way to do it.

You need to specify what field you assign what value to. In the language standard there is no definition of how fields are internally ordered, each JVM implementation could do it differently, so without using an absolute identifier you can't reliably assign parameters to values.

Parameter names are not stored in the java bytecode, thus you cannot assign parameter values from a constructor automagically to fields. You can circumvent this by, e.g., creating setters for each field that adhere to a naming convention you define. But now you can't use a simple constructor anymore and the whole thing is blowing out of proportion...

dot_Sp0T
  • 389
  • 4
  • 26
  • What about things like `GSON` how does that create an entire class from `JSON` without using the above method? – Gary Holiday Dec 09 '17 at 22:12
  • 1
    @GaryHoliday quickly skimming [this tutorial linked on the first github repo with the name GSON I could find](http://www.studytrails.com/java/json/java-google-json-parse-json-to-java/) it seems to be using reflection and a set of rules to access public properties or setters to write data into an object (probably requiring a default-constructor as well). – dot_Sp0T Dec 09 '17 at 22:18
1

I think the problem here is not so much the assignments as the number of distinct fields that this class has to manage. It looks to me like we're managing a name and a date - each of these can be a single instance of a class, a Name class that you whip up for yourself and a java.util.Date (or something fancier if you need something fancier).

age is a straightforward calculation from the birthdate and today's date and can be omitted.

Doing this will allow you to push some complexity - managing the printing of names, doing calendar arithmetic - down into classes which can and should specialize in those specific problems.

Jon Kiparsky
  • 7,499
  • 2
  • 23
  • 38
1

Best practice to don't have such big classes and split them to few smaller. Having constructor that receives more than 3-4 arguments it's the sign that you need to do something with that class. It's very similar to this principle https://en.wikipedia.org/wiki/Interface_segregation_principle

 public MyClass {
        private Person person;

        public MyClass(Person person) {
           this.person = person;
        }

    }

    //when this class will receive more parameters you could
   // be extract name, surname, etc. to Name class 
    class Person {
        private String name;
        private String middleName;
        private String lastName;
        private Date birthDay;

        //constructor

        Date getAge() {
            Date today = new Date();
            return today.difference(birthDay); //something like that
        }

    }


    class Date {
        private int birthDay;
        private int birthMonth;
        private int birthYear;

        //constructor
    }
fxrbfg
  • 1,756
  • 1
  • 11
  • 17
0

It's a good practice passing values in this way. You can take a look to que Oracle docs it have several examples about to pass values to constructor.

And by doing on this way you can validate, convert or whatever you need to do with the information that you send to constructor.

Azac
  • 446
  • 4
  • 8
-1

Applying the Builder Pattern can be used as an alternative to the telescoping constructor problem.

public class MyClassBuilder {

    private MyClass myClass;

    public MyClassBuilder() {
        myClass = new MyClass();
    }

    public MyClassBuilder setFirstName(String firstName) {
       myClass.setFirstName(firstName);
        return this;
    }
    public MyClassBuilder setMiddleName(String middleName) {
        myClass.setMiddleName(middleName);
        return this;
    }
    public MyClassBuilder setLastName(String lastName) {
        myClass.setLastName(lastName);
        return this;
    }
    public MyClassBuilder setAge(int age) {
        myClass.setAge(age);
        return this;
    }
    public MyClassBuilder setBirthDay(int birthDay) {
        myClass.setBirthDay(birthDay);
        return this;
    }
    public MyClassBuilder setBirthMonth(int birthMonth) {
        myClass.setBirthMonth(birthMonth);
        return this;
    }
    public MyClassBuilder setBirthYear(int birthYear) {
        myClass.setBirthYear(birthYear);
        return this;
    }

    public MyClass build() {
        return myClass;
    }
}

Your class would then provide setters for each of the fields, instead of requiring them in the constructor.

public class MyClass {

    private String firstName;
    private String middleName;
    private String lastName;
    private int age;
    private int birthDay;
    private int birthMonth;
    private int birthYear;

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }
    public void setMiddleName(String middleName) {
        this.middleName = middleName;
    }
    public void setLastName(String lastName) {
        this.lastName = lastName;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public void setBirthDay(int birthDay) {
        this.birthDay = birthDay;
    }
    public void setBirthMonth(int birthMonth) {
        this.birthMonth = birthMonth;
    }
    public void setBirthYear(int birthYear) {
        this.birthYear = birthYear;
    }
}
Jeremiah
  • 1,145
  • 6
  • 8
  • 1
    I see how this is different, but it honestly looks like even more bloated code. Is there any advantage to this? – Gary Holiday Dec 09 '17 at 23:17
  • @Jeremiah you know that the builder methods are meant to return the builder object so you can chain methods? – dot_Sp0T Dec 09 '17 at 23:32
  • Applying common design patterns can make code more maintainable, but for a flat Object like you're dealing with it doesn't add much. It may be a bit more valuable if you had additional object composition. If MyClass were composed of a Person (first,middle,last) and a java.util.Date then you could use the builder to accept the fields as you've identified, but then build the composite objects and construct the MyClass reference for you. In that way it can reduce code duplication that the MyClass client is required to write to assemble a reference. – Jeremiah Dec 09 '17 at 23:33
  • @dot_Sp0T thanks for pointing it out. I am aware of that paradigm, just missed it in my careless copy/pasta. – Jeremiah Dec 09 '17 at 23:35