tags:

views:

683

answers:

4
+3  Q: 

Setter Methods

Is it necessary for setter methods to have one argument? Usually setter methods accept one argument as the value of a certain property of an Object. What if I want to test first the validity which depends on another argument which is a boolean, if true, validate first, else just set the value.

I am getting the values from clients through ftp server. Sometimes those files contain garbage values. For instance, a phone number like #3432838#9. So before I set the value I need to remove those garbage characters. Can I do it in the setter methods? Is it a valid approach?

Thanks a bunch in advance!

EDIT:

Is this valid:

public void setSomething(String strValue){ 
     if(checkValidity(strValue)){ 
         // set the value 
     } else { 
         // set the value to an empty string
     }  
  }
+3  A: 

By Java Bean specification setter have one argument. If you add another one, for whatever reason, it is not considered setter anymore.

Setter is perfectly valid to "clean up" its argument, or throw exception if is invalid.

Dev er dev
+10  A: 

It is necessary specifically in the java bean framework model, but it s not mandatory in general.

You can have setter with no argument when they are meant to "swith" a value.

void setCheck()

could for instance be meant to set the "check" boolean attribute to true.

So even if it is not a "setter" in the java bean sense of the term, you can imagine setter used for other purposes.

Plus, according to section 7 of JavaBean specifications, a setter can have more than one argument, for instance for Indexed properties (An indexed property supports a range of values. Whenever the property is read or written you just specify an index to identify which value you want.)

void setter(int index, PropertyType value); // indexed setter
void setter(PropertyType values[]); // array setter


In your case, a valid approach would be to add a runtime exception to the signature of our function.
That way you do not put any unnecessary compilation-time exception checking for all of the other classes which are already calling your setter.

Or you could consider your property as a Constrained property and add a non-runtime exception.

Constrained property setter methods are required to support the PropertyVetoException. This documents to the users of the constrained property that attempted updates may be vetoed. So a simple constrained property might look like:

PropertyType getFoo();
void setFoo(PropertyType value) throws PropertyVetoException;

which allows for VetoableChangeListener to be added if needed.


Regarding your snippet, it is "valid" but may not be optimal because (as said in this question):

  • Validation should be captured separately from getters or setters in a validation method. That way if the validation needs to be reused across multiple components, it is available.
  • It is better to fail fast (hence my proposition to add exception to the setter).
VonC
But void setCheck() is not "setter" anymore. It is just a plain method which name starts with "set".
Dev er dev
True: I just meant that a "setter" is not necessarily attached to Java Bean. It can be declined for other purposes.
VonC
So, is the added code snippet a valid approach?Thank you.
ZiG
+2  A: 

Why not. Verifying and validating the input is a good variant to include into the setter. The question here is, if you want to allow setting the member without validation.

Possibly you need the standard-form of the setter for some framework you use (usage as bean). But if you are not restricted in this way, you could try this.

You could also use asserts in the setter, if you think other code should do the validation but wrong values should never set.

Mnementh
+1  A: 

In the book "Effective Java 2nd Edition" by Joshua Bloch (ISBN-13: 978-0-321-35668-0) saids that it's best to use the builder pattern than the bean convention for objects creations.

For instance (bean pattern):

NutritionFacts cocaCola = new NutritionFacts();
cocaCola.setServingSize(240);
cocaCola.setServings(8);
cocaCola.setCalories(100);
cocaCola.setSodium(35);
cocaCola.setCarbohydrate(27);

Usage with builder pattern:

NutritionFacts cocaCola = new NutritionFacts.Builder(240, 8).
   calories(100).
   sodium(35).
   carbohydrate(27).
   build();

The implementation of builder pattern:

// Builder Pattern
public class NutritionFacts {
    private final int servingSize;
    private final int servings;
    private final int calories;
    private final int fat;
    private final int sodium;
    private final int carbohydrate;
    public static class Builder {
        // Required parameters
        private final int servingSize;
        private final int servings;
        // Optional parameters - initialized to default values
        private int calories = 0;
        private int fat = 0;
        private int carbohydrate = 0;
        private int sodium = 0;
        public Builder(int servingSize, int servings) {
            this.servingSize = servingSize;
            this.servings = servings;
        }
        public Builder calories(int val)
        { calories = val; return this; }
        public Builder fat(int val)
        { fat = val; return this; }
        public Builder carbohydrate(int val)
        { carbohydrate = val; return this; }
        public Builder sodium(int val)
        { sodium = val; return this; }
        public NutritionFacts build() {
            return new NutritionFacts(this);
        }
    }
    private NutritionFacts(Builder builder) {
        servingSize = builder.servingSize;
        servings = builder.servings;
        calories = builder.calories;
        fat = builder.fat;
        sodium = builder.sodium;
        carbohydrate = builder.carbohydrate;
    }
}

When the first two arguments ar required.
For validation you can use early validation (in each <field> method) or lazy validation (in the build() method). And the format is kind of python key-value initialization.

damian
What's the significance of having the build method? Why not just stop at .carbohydrate() ?
Allyn
The build method return a NutritionFacts object. The others ones return a Builder. You can write some thing like this:NutritionFacts n = new NutritionFacts.Builder(12, 15).sodium(12).build();
damian