patternjavaMinor
Determine if an input is an integer, a double or a String
Viewed 0 times
inputdoubledeterminestringinteger
Problem
I created a method to test whether the specified input is a
Is this good code? Could it be improved? I don't like the throwing and catching of
Some test code to prove it works:
Results in:
double, int or String and then pass it to a 3rd party library which has the methods doFoo which accepts either a String, double or int. This is the method that I would like feedback on:public static void testString(String val) {
System.out.print("Original '" + val + "' ");
String x = val.trim();
try {
int i = Integer.parseInt(x);
System.out.println("It's an integer: " + i);
doFoo(i);
} catch (NumberFormatException e) {
try {
double d = Double.parseDouble(x);
System.out.println("It's a double: " + d);
doFoo(d);
} catch (NumberFormatException e2) {
System.out.println("It's a String: " + x);
doFoo(x);
}
}
}Is this good code? Could it be improved? I don't like the throwing and catching of
Exceptions.Some test code to prove it works:
testString("N/A");
testString("19.");
testString("19.0");
testString("19.4");
testString(" 1 ");
testString(" 1");
testString("1 ");
testString("1");
testString(" ");Results in:
Original 'N/A' It's a String: N/A
Original '19.' It's a double: 19.0
Original '19.0' It's a double: 19.0
Original '19.4' It's a double: 19.4
Original ' 1 ' It's an integer: 1
Original ' 1' It's an integer: 1
Original '1 ' It's an integer: 1
Original '1' It's an integer: 1
Original ' ' It's a String:
Solution
I don't like the throwing and catching of Exceptions
This can be made much cleaner with the use of a
However, if this is going to be called hundreds of thousands of times, the
Also, your function is doing multiple things: printing/reporting, and parsing/forwarding to
That was much shorter. Now if you wanted the same functionality, it would look like so:
If you want your code to be extremely extensible, there is another way. Notice how the new function I suggested still does multiple things:
We can separate these into their own components.
This is only really worth it if you can foresee adding types to be a common feature, but especially if the "another function" you forward to should be selectable by the user (say, if you packaged these functions as member functions of an object):
This can be made much cleaner with the use of a
Scanner. It might not be the most performant way, but it's fast and easy to use.try (Scanner scanner = new Scanner(x)) {
if (scanner.hasNextInt()) doFoo(scanner.nextInt());
else if (scanner.hasNextDouble()) doFoo(scanner.nextDouble());
else doFoo(x);
}However, if this is going to be called hundreds of thousands of times, the
try catch method might be faster, though you should encapsulate those into their own functions. You'd need to profile to be sure which is faster, but I believe it would be this because Scanner.hasNextFoo uses regular expressions:public static boolean isInteger(String str) {
try {
Integer.parse(str);
return true;
} catch (NumberFormatException e) {
return false;
}
}Also, your function is doing multiple things: printing/reporting, and parsing/forwarding to
doFoo. This is not a good thing. I'd recommend removing those and handling them where it's more appropriate:public static void testString(String val) {
String x = val.trim();
try (Scanner scanner = new Scanner(x)) {
if (scanner.hasNextInt()) doFoo(scanner.nextInt());
else if (scanner.hasNextDouble()) doFoo(scanner.nextDouble());
else doFoo(x);
}
}That was much shorter. Now if you wanted the same functionality, it would look like so:
public static void testTestString(String val) {
System.out.print("Original '" + val + "' ");
testString(val);
}
// ...
public static void doFoo(int i) {
System.out.println("It's an integer: " + i);
// ...
}If you want your code to be extremely extensible, there is another way. Notice how the new function I suggested still does multiple things:
- It detects the type of the string
- It parses the value from the string
- It forwards the value on to another function
We can separate these into their own components.
This is only really worth it if you can foresee adding types to be a common feature, but especially if the "another function" you forward to should be selectable by the user (say, if you packaged these functions as member functions of an object):
// Class is the easiest type we can return
private static Class determineType(String val) {
try (Scanner scanner = new Scanner(val)) {
if (scanner.hasNextInt()) return Integer.class;
if (scanner.hasNextDouble()) return Double.class;
return String.class;
}
}
private static final Map, Function> parsers = new IdentityHashMap<>();
private static final Map, Consumer> functionSwitch = new IdentityHashMap<>();
static {
parsers.put(Integer.class, Integer::parseInt);
parsers.put(Double.class, Double::parseDouble);
parsers.put(String.class, Function.identity());
// Note that, due to limitations in the type system,
// i is of type Object, so we need to cast it to the appropriate
// class before forwarding on to the function.
functionSwitch.put(Integer.class, i -> doFoo((Integer) i));
functionSwitch.put(Double.class, d -> doFoo((Double) d));
functionSwitch.put(String.class, str -> doFoo((String) str));
}
public static void testString(String val) {
val = val.trim(); // This could even be part of the parser's responsibility
Class stringType = determineType(val);
Function parser = parsers.get(stringType);
functionSwitch.get(stringType).accept(parser.apply(val));
}Code Snippets
try (Scanner scanner = new Scanner(x)) {
if (scanner.hasNextInt()) doFoo(scanner.nextInt());
else if (scanner.hasNextDouble()) doFoo(scanner.nextDouble());
else doFoo(x);
}public static boolean isInteger(String str) {
try {
Integer.parse(str);
return true;
} catch (NumberFormatException e) {
return false;
}
}public static void testString(String val) {
String x = val.trim();
try (Scanner scanner = new Scanner(x)) {
if (scanner.hasNextInt()) doFoo(scanner.nextInt());
else if (scanner.hasNextDouble()) doFoo(scanner.nextDouble());
else doFoo(x);
}
}public static void testTestString(String val) {
System.out.print("Original '" + val + "' ");
testString(val);
}
// ...
public static void doFoo(int i) {
System.out.println("It's an integer: " + i);
// ...
}// Class is the easiest type we can return
private static Class<?> determineType(String val) {
try (Scanner scanner = new Scanner(val)) {
if (scanner.hasNextInt()) return Integer.class;
if (scanner.hasNextDouble()) return Double.class;
return String.class;
}
}
private static final Map<Class<?>, Function<String, ?>> parsers = new IdentityHashMap<>();
private static final Map<Class<?>, Consumer<Object>> functionSwitch = new IdentityHashMap<>();
static {
parsers.put(Integer.class, Integer::parseInt);
parsers.put(Double.class, Double::parseDouble);
parsers.put(String.class, Function.identity());
// Note that, due to limitations in the type system,
// i is of type Object, so we need to cast it to the appropriate
// class before forwarding on to the function.
functionSwitch.put(Integer.class, i -> doFoo((Integer) i));
functionSwitch.put(Double.class, d -> doFoo((Double) d));
functionSwitch.put(String.class, str -> doFoo((String) str));
}
public static void testString(String val) {
val = val.trim(); // This could even be part of the parser's responsibility
Class<?> stringType = determineType(val);
Function<String, ?> parser = parsers.get(stringType);
functionSwitch.get(stringType).accept(parser.apply(val));
}Context
StackExchange Code Review Q#159457, answer score: 6
Revisions (0)
No revisions yet.