views:

2594

answers:

9

I have a project in which we often use Integer.parseInt() to convert a String to an int. When something goes wrong (for example, the String is not a number but the letter 'a', or whatever) this method will throw an exception. However, if I have to put exceptions in my code everywhere, this starts to look very ugly very quickly. I would like to put this in a method, however, I have no clue how to return a clean value in order to show that the convertion went wrong.

In C++ I could have created a method that accepted a pointer to an int and let the method itself return true or false. However, as far as I know, this is not possible in Java. I could also create an object that contains a true/false variable and the converted value, but this does not seem ideal either. The same thing goes for a global value, and this might give me some trouble with multithreading.

So is there a clean way to do this?

+10  A: 

You could return an Integer instead of an int, returning null on parse failure.

It's a shame Java doesn't provide a way of doing this without there being an exception thrown internally though - you can hide the exception (by catching it and returning null), but it could still be a performance issue if you're parsing hundreds of thousands of bits of user-provided data.

EDIT: Code for such a method:

public static Integer tryParse(String text) {
  try {
    return new Integer(text);
  } catch (NumberFormatException e) {
    return null;
  }
}

Note that I'm not sure off the top of my head what this will do if text is null. You should consider that - if it represents a bug (i.e. your code may well pass an invalid value, but should never pass null) then throwing an exception is appropriate; if it doesn't represent a bug then you should probably just return null as you would for any other invalid value.

Jon Skeet
How does this help? The call site will require: <b>temp=tryParse(...); if (temp!=Null) { target=temp; } else { do recovery action };</b> with a probable throw exception in the recovery part. In the original formulation the call site requires <b>try target=(...).parseInt; catch (...) { do recovery action }</b> with a trivial throw exception in the recovery being implemented by simply removing the catch clause. How does the proposed solution make this simpler to understand (it has a magic trick) or reduce the amount of code in any way?
Ira Baxter
It's generally cleaner to code to check for `null` references than it is to handle exceptions on a regular basis.
Adam Maras
It's even cleaner to avoid passing nulls as values but instead somehow indicate that en error has occured; exceptions shouldn't be used for flow control.
Esko
You should use Integer's factory method valueOf(String s).
Steve Kuo
@Steve Kuo: Why? Where's the benefit? They both create a new Integer each time? If anything, I'm tempted to use Integer.parseInt and let autoboxing take care of it, to take advantage of the cache for small values.
Jon Skeet
+1  A: 

My Java is a little rusty, but let me see if I can point you in the right direction:

public class Converter {

    public static Integer parseInt(String str) {
        Integer n = null;

        try {
            n = new Integer(Integer.tryParse(str));
        } catch (NumberFormatException ex) {
            // leave n null, the string is invalid
        }

        return n;
    }

}

If your return value is null, you have a bad value. Otherwise, you have a valid Integer.

Adam Maras
The OP wants the conversion result (as a reference) plus an indication that the conversion was successful (or not).
yawn
@yawn: And a null reference gives exactly that indication.
Jon Skeet
@John Skeet: correct but I read his intent differently. He wrote something like using an intermediate object to differentiate between success/failure + value. Coming from a C++ background I figured if he wanted to use null (instead of an object) he would not have asked the question in the first place.
yawn
There's a major difference between "value" and "object". A null reference is a clean value, but not an object.
Jon Skeet
A: 

You can use a Null-Object like so:

public class Convert {

    @SuppressWarnings({"UnnecessaryBoxing"})
    public static final Integer NULL = new Integer(0);

    public static Integer convert(String integer) {

        try {
            return Integer.valueOf(integer);
        } catch (NumberFormatException e) {
            return NULL;
        }

    }

    public static void main(String[] args) {

        Integer a = convert("123");
        System.out.println("a.equals(123) = " + a.equals(123));
        System.out.println("a == NULL " + (a == NULL));

        Integer b = convert("onetwothree");
        System.out.println("b.equals(123) = " + b.equals(123));
        System.out.println("b == NULL " + (b == NULL));

  Integer c = convert("0");
  System.out.println("equals(0) = " + c.equals(0));
  System.out.println("c == NULL " + (c == NULL));

    }

}

The result of main in this example is:

a.equals(123) = true
a == NULL false
b.equals(123) = false
b == NULL true
c.equals(0) = true
c == NULL false

This way you can always test for failed conversion but still work with the results as Integer instances. You might also want to tweak the number NULL represents (≠ 0).

yawn
What if 'String integer' is the String literal "0"? You'll never know if there was invalid input.
Bart Kiers
A correct parse of 0 will be ≠ NULL but equals(0).
yawn
I guess that depends on whether the == operator for two Integers compares values or references. If it compares values, the problem exists. If it compares references, it would work in a way equivalent to my answer.
Adam Maras
Why the downvote?My answer is correct and offers the advantage (over null) that you always deal with a valid instance of Integer (instead of null) relieving you of having to deal with NPEs.
yawn
Distinguishing null from a real integer is *useful* though. You *should* be testing the result for nullity, to know whether the parse succeeded or not. Hiding that behind and otherwise usable object is a recipe for problems, IMO.
Jon Skeet
More downvotes - interesting! Since /me being new to SO and all could one of the voters explain to me the why?
yawn
Jon's got a point about the usefulness of your predefined `NULL` value. Here's an example: if he uses your solution and forgets to check for equality to your `NULL` object, his program will work flawlessly, but erroneously interpret the parsed value as 0. With my solution, if he forgets to check for a `null` reference, his program will (correctly) crash at runtime, reminding him that he needs to insert the proper behavior for the results of an invalid parsing.
Adam Maras
@Adam: true! But the OP *did* ask for a solution returning a valid instance, did he not?
yawn
No, he asked for "a clean value in order to show that the convertion went wrong," and `null` is a clean value when you're using an `Integer` return type.
Adam Maras
@Adam: I read/interpreted it differently. Still I don't get -2 - it's not like the answer was a troll post/spam whatever.@Jon: Please delete the entire answer if possible. -2 looks like it's suggesting something blatantly wrong/stupid which it clearly does not.
yawn
+2  A: 

May be you can use something like this:

public class Test {
public interface Option<T> {
 T get();

 T getOrElse(T def);

 boolean hasValue();
}

final static class Some<T> implements Option<T> {

 private final T value;

 public Some(T value) {
  this.value = value;
 }

 @Override
 public T get() {
  return value;
 }

 @Override
 public T getOrElse(T def) {
  return value;
 }

 @Override
 public boolean hasValue() {
  return true;
 }
}

final static class None<T> implements Option<T> {

 @Override
 public T get() {
  throw new UnsupportedOperationException();
 }

 @Override
 public T getOrElse(T def) {
  return def;
 }

 @Override
 public boolean hasValue() {
  return false;
 }

}

public static Option<Integer> parseInt(String s) {
 Option<Integer> result = new None<Integer>();
 try {
  Integer value = Integer.parseInt(s);
  result = new Some<Integer>(value);
 } catch (NumberFormatException e) {
 }
 return result;
}

}

Nikolay Ivanov
A: 

You could also replicate the C++ behaviour that you want very simply

public static boolean parseInt(String str, int[] byRef) {
    if(byRef==null) return false;
    try {
       byRef[0] = Integer.parseInt(prop);
       return true;
    } catch (NumberFormatException ex) {
       return false;
    }
}

You would use the method like so:

int[] byRef = new int[1];
boolean result = parseInt("123",byRef);

After that the variable result it's true if everything went allright and byRef[0] contains the parsed value.

Personally, I would stick to catching the exception.

+3  A: 

After reading the answers to the question I think encapsulating or wrapping the parseInt method is not necessary, maybe even not a good idea.

You could return 'null' as Jon suggested, but that's more or less replacing a try/catch construct by a null-check. There's just a slight difference on the behaviour if you 'forget' error handling: if you don't catch the exception, there's no assignment and the left hand side variable keeps it old value. If you don't test for null, you'll probably get hit by the JVM (NPE).

yawn's suggestion looks more elegant to me, because I do not like returning null to signal some errors or exceptional states. Now you have to check referential equality with a predefined object, that indicates a problem. But, as others argue, if again you 'forget' to check and a String is unparsable, the program continous with the wrapped int inside your 'ERROR' or 'NULL' object.

Nikolay's solution is even more object orientated and will work with parseXXX methods from other wrapper classes aswell. But in the end, he just replaced the NumberFormatException by an OperationNotSupported exception - again you need a try/catch to handle unparsable inputs.

So, its my conclusion to not encapsulate the plain parseInt method. I'd only encapsulate if I could add some (application depended) error handling as well.

Andreas_D
+2  A: 

What behaviour do you expect when it's not a number?

If, for example, you often have a default value to use when the input is not a number, then a method such as this could be useful:

public static int parseWithDefault(String number, int default) {
  try {
    return Integer.parseInt(number);
  } catch (NumberFormatException e) {
    return default;
  }
}

Similar methods can be written for different default behaviour when the input can't be parsed.

Joachim Sauer
A: 

I would suggest you consider a method like

 IntegerUtilities.isValidInteger(String s)

which you then implement as you see fit. If you want the result carried back - perhaps because you use Integer.parseInt() anyway - you can use the array trick.

 IntegerUtilities.isValidInteger(String s, int[] result)

where you set result[0] to the integer value found in the process.

Thorbjørn Ravn Andersen
A: 

This is somewhat similar to Nikolay's solution:

 private static class Box<T> {
  T me;
  public Box() {}
  public T get() { return me; }
  public void set(T fromParse) { me = fromParse; }
 }

 private interface Parser<T> {
  public void setExclusion(String regex);
  public boolean isExcluded(String s);
  public T parse(String s);
 }

 public static <T> boolean parser(Box<T> ref, Parser<T> p, String toParse) {
  if (!p.isExcluded(toParse)) {
   ref.set(p.parse(toParse));
   return true;
  } else return false;
 }

 public static void main(String args[]) {
  Box<Integer> a = new Box<Integer>();
  Parser<Integer> intParser = new Parser<Integer>() {
   String myExclusion;
   public void setExclusion(String regex) {
    myExclusion = regex;
   }
   public boolean isExcluded(String s) {
    return s.matches(myExclusion);
   }
   public Integer parse(String s) {
    return new Integer(s);
   }
  };
  intParser.setExclusion("\\D+");
  if (parser(a,intParser,"123")) System.out.println(a.get());
  if (!parser(a,intParser,"abc")) System.out.println("didn't parse "+a.get());
 }

The main method demos the code. Another way to implement the Parser interface would obviously be to just set "\D+" from construction, and have the methods do nothing.

Carl