简体   繁体   中英

Java constructor bypass setter validation

When learning Java constructor and mutator, I found out that setter can be used to perform validation. But if we directly use constructor to create a new instance, wouldn't it bypass the validation in setter? Below is the sample code:

public static void main(String[] args){
    Person p1 = new Person("My Name", "My Gender"); //bypass setter validation
    p1.setGender("Female"); //validation is performed
}

public class Person{
    public Person(String name, String gender){
        this.name = name;
        this.gender = gender;
    }

    public void setGender(String gender){
        if(gender.equals("Male") || gender.equals("Female")){
            this.gender = gender;
        }
        else{
            this.gender = "undefined";
        }
    }
}

I have seen some mentioned that you can call the setter in the constructor, but seems like it is not a good approach as a lot of forums mentioned that it will cause some "overridden" issue. If that's the case, is there anything I can do to make sure that the setter validation can be performed when I'm calling my constructor?

Thanks in advance!

Calling the setter in the constructor should only be done if either the class or the setter is final. Otherwise a sub class may override it, and do other things that attempt to use fields that haven't been initialized yet.

One easy fix is to make the field final. You then only have to validate it in the constructor, because there is no need for a setter. Another possibility is to duplicate the validation. But then you're duplicating code, and your first instinct should be to refactor it into a utility method.

Which can lead to something like this (and I'm swapping the operands to the equals calls to be null -safe):

public class Person {
    public Person(String name, String gender) {
        this.name = name;
        setValidGender(gender);
    }

    public void setGender(String gender) {
        setValidGender(gender);
    }

    private void setValidGender(String gender) {
        if ("Male".equals(gender) || "Female".equals(gender)) {
            this.gender = gender;
        } else {
            this.gender = "undefined";
        }
    }
}
   public boolean validateGender(String p_gender){
    if(p_gender.equals("Male") || p_gender.equals("Female")){
       return true;
    }
    else{
       return false;
    }

So, how about something like the above?

Then, use that in both setGender(String gender) and the constructor that you're looking to perform the validation in?

public Person(String name, String gender){
    this.name = name;
    if(validateGender(gender)){
       this.gender = gender;
    }
    else{
       this.gender = "undefined";
    }
}

Use the builder pattern and do the validation as a part of the builder.

So here's the deal. Performing field validation while the object is being instantiated is dangerous because you want to avoid two things:

  • Invalid data is in a "valid" object (do not assume a default gender)
  • Object is half-instantiated (if you throw an exception during object construction, weird things happen

So instead, use a separate object that builds it out for you, and does the validation in place.

public PersonBuilder {
    public Person person = new Person(); // blank person object
    public static PersonBuilder getInstance() {
        return new PersonBuilder();
    }

    public PersonBuilder withName(final String name) {
        // do your validation here, and if it fails, you can blow up
        person.setName(name);
        return this;
    }

    public PersonBuilder withGender(final String gender) {
        // do your validation here, and if it fails, you can blow up
        person.setGender(gender);
        return this;
    }

    public Person build() {
        return person;
    }

}

// Usage

Person person = PersonBuilder.getInstance().withName("Tom").withGender("Male").build();

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM