18

I'm doing a basic Java course and I came to a problem: How do I create an object only if I have passed valid parameters to the Constructor?

Should I make an alternate class and call the constructor from there after the validation is realized?

Or should/could I use a static method in the class for the validation?

What is the best practice in this case?

Mureinik
  • 297,002
  • 52
  • 306
  • 350
Tiago Duque
  • 1,956
  • 1
  • 12
  • 31

7 Answers7

28

The standard practice is to validate the arguments in the constructor. For example:

class Range {
  private final int low, high;
  Range(int low, int high) {
    if (low > high) throw new IllegalArgumentException("low can't be greater than high");
    this.low = low;
    this.high = high;
  }
}

Side note: to verify that arguments are not null, which is fairly common, you can use:

import static java.util.Objects.requireNonNull;

Constructor(Object o) {
  this.o = requireNonNull(o); //throws a NullPointerException if 'o' is null
}

UPDATE

To reply to your specific comment about social security number. One way would be to add a method to the class:

//constructor
public YourClass(String ssn) {
  if (!isValidSSN(ssn)) throw new IllegalArgumentException("not a valid SSN: " + ssn);
  this.ssn = ssn;
}

public static boolean isValidSSN(String ssn) {
  //do some validation logic
}

The calling code could then look like:

String ssn = getSsnFromUser();
while(!YourClass.isValidSSN(ssn)) {
  showErrorMessage("Not a valid ssn: " + ssn);
  ssn = getSsnFromUser();
}
//at this point, the SSN is valid:
YourClass yc = new YourClass(ssn);

With that design, you have achieved two things:

  • you validate the user input before using it (which you should always do - users are very good at typos)
  • you have made sure that if YourClass is misused an exception is thrown and it will help you detect bugs

You could go further by creating a SSN class that holds the SSN and encapsulates the validation logic. YourClass would then accept a SSN object as an argument which is always a valid SSN by construction.

assylias
  • 321,522
  • 82
  • 660
  • 783
  • The illegal argument, as you and others said would break the program, right? – Tiago Duque Jun 12 '15 at 13:08
  • @TiagoSirious What do you mean by "break"? It will exit the constructor immediately and throw an exception to the caller. And you want that because you received arguments that you don't know what to do with... So that will help you identify a bug in the code that is calling this constructor. – assylias Jun 12 '15 at 13:14
  • Ok, but imagine that the user passes a Social Security Number that is not valid. I want it checked, but I dont want the application to stop working. I want to warn the user that this SSN is not valid and I want it to reinsert a valid SSN. In this case, if I throw the exception, is it just the constructor that exits or the whole application? – Tiago Duque Jun 12 '15 at 13:22
  • Update: I used try catch to catch the exception and print a String. Now it is not breaking the application anymore. Is this a good practice for the case I stated above? – Tiago Duque Jun 12 '15 at 13:26
  • 5
    To avoid null values I would use @NotNull annotation – Arthur Eirich Jun 12 '15 at 13:30
  • The exception will cause the app to crash unless the exception is caught. So make sure it's never thrown in the first place. That's why you need to validate the parameters before calling the constructor. The parameters need to come from somewhere. If they come from a GUI, display a warning. If they come from a client, return an error response. – Kirill Rakhman Jun 12 '15 at 13:43
  • Ok, so try catch is not a good Idea for it had to be treated beforehand. Also, if we have several modules that could enter SSN, the best practice would be to encapsulate it and have a separate class just for the ssn as in the edit, right? – Tiago Duque Jun 12 '15 at 19:07
  • Yes that's the idea - try catch is for unexpected situations. A bad user entry is an expected situation that you should prevent. – assylias Jun 12 '15 at 19:24
  • 4
    +1 for your update. One of the rules I teach younger coders is "exception handling is for exceptional occurrences." There is no shame in properly handling the SSN and asking for corrections, and then having the constructor ALSO check and handle any surprises as exceptional. The only place I'd do anything else is in performance-critical code where the checks could take a large portion of the total runtime budget. In those cases, Danikov's answer of "Don't use Exceptions for flow control" becomes the right solution, and I'd handle everything with flow control and no exceptions. – Cort Ammon Jun 12 '15 at 23:15
9

I'd just throw an IllegalArgumentException in the constructor itself:

public class MyClass {
    private int i;

    public MyClass (int i) {
        // example validation:
        if (i < 0) {
            throw new IllegalArgumentException ("i mustn't be negatve!");
        }
        this.i = i;
}
Mureinik
  • 297,002
  • 52
  • 306
  • 350
8

A well-known truism in programming is 'Don't use Exceptions for flow control'. Your code should be aware of the restrictions and guard against them before calling the constructor rather than handling errors. Exceptions exist for expectational circumstances, especially ones that cannot be predicted or guarded against (for example, an IO stream may become invalid during writing, despite being OK during a previous check).

While you can throw exceptions in your constructor, this is not always ideal. If you are writing public objects that you expect to be used/reused by others, Exceptions are the only real option for public constructors, however such limitations and their result (e.g. what exception will be thrown) should be clearly documented in the javadoc for the class.

For internal classes, assertions are more appropriate. As Oracle states: "Assertions... should be used to check for cases that should never happen, check assumptions about data structures, or enforcing constraints on arguments of private methods."—Using Assertions in Java Technology. You probably should still document your expectations for the class, but your application should internally do any checks beforehand rather than relying on any Exceptions being thrown.

Static factory methods can help a little, their benefits are elaborating upon a bit by another question: How to use “Static factory methods” instead of constructors. However, they don't give strong validation options without, again, relying on Exceptions when things are not valid (that, or returning null, which is less informative).

Your ideal solution is the Builder pattern. Not only does it allow for a greater deal of flexibility in managing your arguments, you may validate each one individually, or have a validate method that can evaluate all the fields at once. A builder can and should be used to hide the object's actual constructor, enjoying sole access to it and preventing any unwanted values from ever being submitted, while assertions can guard against 'the builder should never submit these values'.

Community
  • 1
  • 1
Danikov
  • 735
  • 3
  • 10
  • I find the phrase "don't use exceptions for flow-control" nonsensical: As the throw statement is a control flow statement (it transfers control to some other code block), this would mean that user code must never throw an exception. – meriton Jun 14 '15 at 13:20
  • @meriton I believe there's an implied 'normal' in there, that is to say, "don't use exceptions for **normal** flow-control". The implication is that, if an if statement can do the same job, use that instead, as qualified in my answer. – Danikov Jun 14 '15 at 18:33
  • Every exception can be replaced by an if-statement (C programmers get by without exceptions, after all). – meriton Jun 14 '15 at 18:49
4

Constructors can throw exceptions (see Can constructors throw exceptions in Java?) so you can have your constructor throwing an exception if invalid values are passed. You can also make your constructor private and use the static method to create your object, that performs the checks. This might be cleaner.

Community
  • 1
  • 1
JP Moresmau
  • 7,388
  • 17
  • 31
1

One way to make sure you have valid parameters passed to the constructor is to create the parent class with constructors that only accept the parameters you require, then create a subclass that your end-users use. If you force your user to call super() and pass in your required parameters, then they have to at least pass in the right data objects. As far as valid values for those parameters, that's up to you whether you want to include validation in the parent class constructor and throw runtime exceptions or whatnot.

Here's an example of the superclass / subclass thing. Let's call the superlcass SomeShape and the subclass Triangle. For any SomeShape object, you are going to force the "user" to provide a number of sides and a side length. This is how...

public class SomeShape {
    private int numSides;
    private int sideLength;

    public SomeShape(int mNumSides, int mSideLength) {
        numSides = mNumSides;
        sideLength = mSideLength;
    }
}

public class Triangle extends SomeShape {
    private int height;

    public Triangle(int mNumSides, int mSideLength, int mHeight) {
        super(mNumSides, mSideLength);
        height = mHeight;
    }   
}

Aside from hard-coding a bunch of logic and exception throwing into your constructor, this is a relatively clean way to enforce what parameters are required to create the object.

Chamatake-san
  • 551
  • 3
  • 10
0

If you don't want to throw an exception from the constructor, you could make the constructor private and create a static method that returns a new instance of the object, or null if the arguments are invalid. The caller of this method would have to check if the result is null or not, however.

Example:

public class Foo {
  private Foo(int arg1, Bar arg2) {
    // guaranteed to be valid.
  }

  public static Foo construct(int arg1, Bar arg2) {
    // perform validation
    if (arg1 < 0 || arg2 == null) {
      return null;
    } else {
      return new Foo(arg1, arg2);
    }
  }
}

Usage

Foo object = Foo.construct(1, new Bar());
if (object == null) {
  // handle error here.
}
MusicMaster
  • 549
  • 4
  • 14
-2

It's bad practice to throw an exception out of a constructor. You end up with a partially initialized object, which is probably going to break all kinds of contracts.

If a constructor isn't valid for all combinations of inputs, it's cleaner to create a factory method that does the validation, and make the constructor private. If there's a real possibility of failure (that is, the failure isn't due to a programming error), then it might be appropriate to return an Optional.

MattPutnam
  • 2,927
  • 2
  • 17
  • 23
  • 1
    If there is no constructor leak(when you assign a static variable or pass **this** instance to another thread - which is bad practice anyway), then the object will be garbage collected without consequences. – Boris Treukhov Jun 12 '15 at 22:44
  • 2
    If a constructor throws an exception, the new object is not returned to the caller, and can cause no harm (unless of course the constructor leaks a reference to the object under construction to other code before it is fully initialized, which is a bad practice anyway). – meriton Jun 14 '15 at 13:32