guo
guo

Reputation: 10229

How to know which variable is the culprit in try block?

In a certain try block, I have two String variables which could cause NumberFormatException when I user Integer.parseInt(string1) andInteger.parseInt(string2). The question is, if I catch an exception, how to know which string is the troublemaker? I need to get the troublemaker's variable name.

Here is some example code:

public class test {
    public static void main(String[] args) {
        try {
            String string1 = "fdsa";
            String string2 = "fbbbb";
            Integer.parseInt(string1);
            Integer.parseInt(string2);
        } catch (NumberFormatException e) {
            e.printStackTrace();
        }
        }
    }

And the method e.printStackTrace() doesn't tell me the variable name; it just tells me the content of the troublemaker.

java.lang.NumberFormatException: For input string: "fdsa" at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65) at java.lang.Integer.parseInt(Integer.java:580) at java.lang.Integer.parseInt(Integer.java:615) at test.main(test.java:9) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at com.intellij.rt.execution.application.AppMain.main(AppMain.java:147)

Process finished with exit code 0

The reason that I need to know the variable's name is that I need to prompt the user what's going on. For instance, tell the user that string1 is wrong by using

System.out.println(troubleMakerName + "is wrong!")

In my Requirements, the user should input

fd=(fileName,maxLength,minLength)

then I will analyse the input string and create some responses. So I'd like to check whether the maxLength and minLength will throw NumberFormatException. In this case, if minLength has something wrong, then I need to prompt the user that the minLength is wrong.

Upvotes: 41

Views: 4614

Answers (9)

Polygnome
Polygnome

Reputation: 7820

You are having an XY-Problem.

You don't want to read the actual variable name. You want to be able to validate input and give reasonable error messages to your user.

String fileName, maxLengthInput, minLengthInput;
int maxLength, minLength;

List<String> errors = new ArrayList<>();

try {
    maxLength = Integer.parseInt(maxlengthInput);
} catch (NumberFormatException nfe) {
    errors.add("Invalid input for maximum length, input is not a number");
}

try {
    minLength = Integer.parseInt(minlengthInput);
} catch (NumberFormatException nfe) {
    errors.add("Invalid input for minimum length, input is not a number");
}

// show all error strings to the user

Not throwing the exceptions directly but collecting them allows you to notify the user about all invalid inputs at once (maybe highlight the related fields with red color) instead of having them fix one input, trying to submit again, and then to see that another input is also wrong.

Instead of Strings you could user your own data struture containing information of the related field etc., but that quickly gets out of scope. The main gist is: use two try-catch blocks, and you are able to differentiate which field is errorneous.

If more inputs are involved, you can refactor this into a loop.

Upvotes: 70

Khaled.K
Khaled.K

Reputation: 5960

It's quite simplistic, but here's my solution..

public class test
{
    public static int tryParseInt(String str) throws Exception
    {
        try
        {
            return Integer.parseInt(str);
        }
        catch(Exception e)
        {
            System.err.println("tryParseInt: couldn't parse '"+str+"'");
            throw e;
        }
    }

    public static void main(String[] args)
    {
        try
        {
            String string1 = "fdsa";
            String string2 = "fbbbb";

            tryParseInt(string1);
            tryParseInt(string2);
        }
        catch (NumberFormatException e)
        {
            e.printStackTrace();
        }
    }
}

Upvotes: 0

Branko Radovanović
Branko Radovanović

Reputation: 29

I'm surprised nobody proposed something like this:

public class test {
public static void main(String[] args) {
    String errorContext;

    try {            
        String string1 = "fdsa";
        String string2 = "fbbbb";

        errorContext = "string1";
        Integer.parseInt(string1);

        errorContext = "string2";
        Integer.parseInt(string2);
    } catch (NumberFormatException e) {
        System.out.println(errorContext + " is wrong!")
        e.printStackTrace();
    }
    }
}

It is a very simple solution - simplistic, some would say - but it is also fairly clear and robust.

The point of this question - as I understand it - was not to provide a better way of converting strings to integers (even if it indeed may exist), but rather to find a way to say not only what went wrong in a long try block, but also where it went wrong. If one's goal is to display a meaningful error message to the user (ideally suggesting what to do rather than just complaining that something is wrong), then simply printing a stack trace is not sufficient, of course, and try-catching every line of code is not an attractive option either.

Upvotes: 2

Black
Black

Reputation: 20362

Just use another try catch for the other statement

public class test 
{
    public static void main(String[] args) 
    {
        String string1 = "fdsa";
        String string2 = "fbbbb";

        try 
        {
            Integer.parseInt(string1);
        } 
        catch (NumberFormatException e) 
        {
            System.out.println("string1 is the culprit");
            e.printStackTrace();
        }

        try 
        {
            Integer.parseInt(string2);
        } 
        catch (NumberFormatException e) 
        {
            System.out.println("string2 is the culprit");
            e.printStackTrace();
        }
    }
}

Upvotes: 3

Andrew
Andrew

Reputation: 49656

I would like to write own parseInt method:

public static int parseInt(String s, 
                           Supplier<? extends RuntimeException> supplier) {
    try {
        return Integer.parseInt(s);
    } catch (NumberFormatException e) {
        throw (RuntimeException)supplier.get().initCause(e);
    }
}

As I know, we can't read a variable name by reflection, so I just pass a String literal instead:

parseInt(string1, () -> new NumberFormatException("string1"));

I will leave the origin answer and provide a version that has discussed in the comments. But for now, I am confused why a Supplier is an overkill.

public static int parseInt(String s, String message) {
    try {
        return Integer.parseInt(s);
    } catch (NumberFormatException e) {
        throw (NumberFormatException)new NumberFormatException(message).initCause(e);
        // throw new IllegalArgumentException(message, e);
    }
}

Its call looks like

parseInt(string1, "string1");

Upvotes: 6

Luke
Luke

Reputation: 848

You could create a method with a try/catch that returns the value you assign or prints to console that there was a problem, this way you could accommodate for as many variables as you need with one try/catch while still tracking the name of all variables that cause a problem.

public class test {
    public static void main(String[] args) {
        String string1 = returnString("fdsa", "string1");
        String string2 = returnString("fbbbb", "string2");
    }

    private string returnString(String input, String varName) {
        String str = "";
        try {
            str = input;
            Integer.parseInt(str);
        } catch (NumberFormatException e) {
            System.out.println("Error processing " + varName);
        }
        return str;
    }
}

Upvotes: 2

Heisenberg
Heisenberg

Reputation: 3193

You should avoid catching Runtime exceptions and use some other mechanism for identifying errors. In your case you could use matcher and your problem could be written as:

public class test {

    private static final Pattern pattern = Pattern.compile("\\d+");
    public static void main(String[] args) {
            String string1 = "fdsa";
            String string2 = "fbbbb";
            if (pattern.matcher(string1).matches()) {
                Integer.parseInt(string1);
            } else {
                //string1 is not an integer
            }
            ...
    }
}

Or you could just write

boolean errorInLineOne = !pattern.matcher(string1).matches();
...

Upvotes: 0

Chand Priyankara
Chand Priyankara

Reputation: 6782

Use 2 seperate try,catch blocks to parse two inputs for each variables. Then generate the sanity check message inside each catch block.

        String string1 = "fdsa";
        String string2 = "fbbbb";
        try {
            Integer.parseInt(string1);
        } catch (NumberFormatException e) {
            e.printStackTrace();
            **//Please provide a valid integer for string1**
        }
        try {
            Integer.parseInt(string2 );
        } catch (NumberFormatException e) {
            e.printStackTrace();
           **//Please provide a valid integer for string2** 
        }

Upvotes: 10

Ramachandran.A.G
Ramachandran.A.G

Reputation: 4948

Before you perform the operation you can write a simple isInteger() function that can return you a boolean. A good implementation can be found on this thread. This uses the radix of the value and iterates if it is an int and is quite handy. Determine if a String is an Integer in Java A simple if conditional then can find which value is rogue in the argument

Upvotes: 4

Related Questions