parsestring java exception-handling encapsulation

java - parsestring - Buena forma de encapsular Integer.parseInt()



integer.parseint joptionpane (22)

Tengo un proyecto en el que a menudo utilizamos Integer.parseInt() para convertir un String en un int. Cuando algo sale mal (por ejemplo, la String no es un número sino la letra a , o lo que sea) este método emitirá una excepción. Sin embargo, si tengo que manejar excepciones en mi código en todas partes, esto empieza a verse muy feo muy rápidamente. Me gustaría poner esto en un método, sin embargo, no tengo ni idea de cómo devolver un valor limpio para mostrar que la conversión salió mal.

En C ++ podría haber creado un método que aceptara un puntero a un int y dejara que el método devuelva verdadero o falso. Sin embargo, hasta donde yo sé, esto no es posible en Java. También podría crear un objeto que contenga una variable verdadero / falso y el valor convertido, pero tampoco parece ideal. Lo mismo ocurre con un valor global, y esto podría darme algunos problemas con multihilo.

Entonces, ¿hay una manera limpia de hacer esto?


¿Qué comportamiento espera cuando no es un número?

Si, por ejemplo, a menudo tiene un valor predeterminado para usar cuando la entrada no es un número, entonces un método como este podría ser útil:

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

Se pueden escribir métodos similares para diferentes comportamientos predeterminados cuando la entrada no se puede analizar.


¿Qué hay de bifurcar el método parseInt ?

Es fácil, simplemente copie y pegue los contenidos a una nueva utilidad que devuelve Integer u Optional<Integer> y reemplace los lanzamientos con devoluciones. Parece que no hay excepciones en el código subyacente, pero es mejor verificarlo .

Al omitir todo el manejo de excepciones, puede ahorrar algo de tiempo en entradas no válidas. Y el método está allí desde JDK 1.0, por lo que no es probable que deba hacer mucho para mantenerlo actualizado.


Después de leer las respuestas a la pregunta, creo que encapsular o ajustar el método parseInt no es necesario, tal vez ni siquiera una buena idea.

Podrías devolver ''nulo'' como Jon sugirió, pero eso es más o menos reemplazar una construcción try / catch por un cheque nulo. Hay una pequeña diferencia en el comportamiento si ''olvidas'' el manejo de errores: si no captas la excepción, no hay asignación y la variable del lado izquierdo mantiene el valor anterior. Si no prueba nulo, es probable que sea golpeado por la JVM (NPE).

La sugerencia de bostezo me parece más elegante, porque no me gusta devolver nulo para señalar algunos errores o estados excepcionales. Ahora debe verificar la igualdad referencial con un objeto predefinido, que indica un problema. Pero, como otros argumentan, si una vez más ''olvidas'' verificar y una cadena no se puede analizar, el programa continuará con el int dentro de tu objeto ''ERROR'' o ''NULO''.

La solución de Nikolay está aún más orientada a objetos y también funcionará con métodos parseXXX de otras clases contenedoras. Pero al final, él acaba de reemplazar la excepción NumberFormatException por una excepción OperationNotSupported. De nuevo, necesita un try / catch para manejar las entradas no analizables.

Entonces, es mi conclusión de no encapsular el método de parseInt simple. Solo encapsularía si pudiera agregar algún tipo de manejo de error (dependiente de la aplicación) también.


En algunos casos, debe manejar los errores de análisis como situaciones de falla, pero en otros casos, como la configuración de la aplicación, prefiero manejar la entrada faltante con valores predeterminados usando Apache Commons Lang 3 NumberUtils .

int port = NumberUtils.toInt(properties.getProperty("port"), 8080);


Esta es una respuesta a la pregunta 8391979, "¿Java tiene una int.tryparse que no arroja una excepción para los datos erróneos? [Duplicado]" que está cerrado y vinculado a esta pregunta.

Editar 2016 08 17: Agregué métodos ltrimZeroes y los llamé en tryParse (). Sin ceros iniciales en numberString puede dar resultados falsos (ver comentarios en el código). Ahora también hay un método público String ltrimZeroes (String numberString) que funciona para los "números" positivos y negativos (END Edit)

A continuación, encontrará una clase Wrapper (boxeo) rudimentaria para int con un método tryParse () de alta velocidad optimizado (similar a C #) que analiza la cadena en sí y es un poco más rápido que Integer.parseInt (String s) de Java:

public class IntBoxSimple { // IntBoxSimple - Rudimentary class to implement a C#-like tryParse() method for int // A full blown IntBox class implementation can be found in my Github project // Copyright (c) 2016, Peter Sulzer, Fürth // Program is published under the GNU General Public License (GPL) Version 1 or newer protected int _n; // this "boxes" the int value // BEGIN The following statements are only executed at the // first instantiation of an IntBox (i. e. only once) or // already compiled into the code at compile time: public static final int MAX_INT_LEN = String.valueOf(Integer.MAX_VALUE).length(); public static final int MIN_INT_LEN = String.valueOf(Integer.MIN_VALUE).length(); public static final int MAX_INT_LASTDEC = Integer.parseInt(String.valueOf(Integer.MAX_VALUE).substring(1)); public static final int MAX_INT_FIRSTDIGIT = Integer.parseInt(String.valueOf(Integer.MAX_VALUE).substring(0, 1)); public static final int MIN_INT_LASTDEC = -Integer.parseInt(String.valueOf(Integer.MIN_VALUE).substring(2)); public static final int MIN_INT_FIRSTDIGIT = Integer.parseInt(String.valueOf(Integer.MIN_VALUE).substring(1,2)); // END The following statements... // ltrimZeroes() methods added 2016 08 16 (are required by tryParse() methods) public static String ltrimZeroes(String s) { if (s.charAt(0) == ''-'') return ltrimZeroesNegative(s); else return ltrimZeroesPositive(s); } protected static String ltrimZeroesNegative(String s) { int i=1; for ( ; s.charAt(i) == ''0''; i++); return ("-"+s.substring(i)); } protected static String ltrimZeroesPositive(String s) { int i=0; for ( ; s.charAt(i) == ''0''; i++); return (s.substring(i)); } public static boolean tryParse(String s,IntBoxSimple intBox) { if (intBox == null) // intBoxSimple=new IntBoxSimple(); // This doesn''t work, as // intBoxSimple itself is passed by value and cannot changed // for the caller. I. e. "out"-arguments of C# cannot be simulated in Java. return false; // so we simply return false s=s.trim(); // leading and trailing whitespace is allowed for String s int len=s.length(); int rslt=0, d, dfirst=0, i, j; char c=s.charAt(0); if (c == ''-'') { if (len > MIN_INT_LEN) { // corrected (added) 2016 08 17 s = ltrimZeroesNegative(s); len = s.length(); } if (len >= MIN_INT_LEN) { c = s.charAt(1); if (!Character.isDigit(c)) return false; dfirst = c-''0''; if (len > MIN_INT_LEN || dfirst > MIN_INT_FIRSTDIGIT) return false; } for (i = len - 1, j = 1; i >= 2; --i, j *= 10) { c = s.charAt(i); if (!Character.isDigit(c)) return false; rslt -= (c-''0'')*j; } if (len < MIN_INT_LEN) { c = s.charAt(i); if (!Character.isDigit(c)) return false; rslt -= (c-''0'')*j; } else { if (dfirst >= MIN_INT_FIRSTDIGIT && rslt < MIN_INT_LASTDEC) return false; rslt -= dfirst * j; } } else { if (len > MAX_INT_LEN) { // corrected (added) 2016 08 16 s = ltrimZeroesPositive(s); len=s.length(); } if (len >= MAX_INT_LEN) { c = s.charAt(0); if (!Character.isDigit(c)) return false; dfirst = c-''0''; if (len > MAX_INT_LEN || dfirst > MAX_INT_FIRSTDIGIT) return false; } for (i = len - 1, j = 1; i >= 1; --i, j *= 10) { c = s.charAt(i); if (!Character.isDigit(c)) return false; rslt += (c-''0'')*j; } if (len < MAX_INT_LEN) { c = s.charAt(i); if (!Character.isDigit(c)) return false; rslt += (c-''0'')*j; } if (dfirst >= MAX_INT_FIRSTDIGIT && rslt > MAX_INT_LASTDEC) return false; rslt += dfirst*j; } intBox._n=rslt; return true; } // Get the value stored in an IntBoxSimple: public int get_n() { return _n; } public int v() { // alternative shorter version, v for "value" return _n; } // Make objects of IntBoxSimple (needed as constructors are not public): public static IntBoxSimple makeIntBoxSimple() { return new IntBoxSimple(); } public static IntBoxSimple makeIntBoxSimple(int integerNumber) { return new IntBoxSimple(integerNumber); } // constructors are not public(!=: protected IntBoxSimple() {} { _n=0; // default value an IntBoxSimple holds } protected IntBoxSimple(int integerNumber) { _n=integerNumber; } }

Prueba / programa de ejemplo para la clase IntBoxSimple:

import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; public class IntBoxSimpleTest { public static void main (String args[]) { IntBoxSimple ibs = IntBoxSimple.makeIntBoxSimple(); String in = null; BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); do { System.out.printf( "Enter an integer number in the range %d to %d:%n", Integer.MIN_VALUE, Integer.MAX_VALUE); try { in = br.readLine(); } catch (IOException ex) {} } while(! IntBoxSimple.tryParse(in, ibs)); System.out.printf("The number you have entered was: %d%n", ibs.v()); } }


Esto es algo similar a la solución de Nikolay:

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()); }

El método principal muestra el código. Otra forma de implementar la interfaz del Analizador sería simplemente establecer "/ D +" desde la construcción, y hacer que los métodos no hagan nada.


Hay Ints.tryParse() en Guava . No lanza una excepción en una cadena no numérica, sin embargo, lanza una excepción en una cadena nula.


La forma en que manejo este problema es recursiva. Por ejemplo, al leer datos de la consola:

Java.util.Scanner keyboard = new Java.util.Scanner(System.in); public int GetMyInt(){ int ret; System.out.print("Give me an Int: "); try{ ret = Integer.parseInt(keyboard.NextLine()); } catch(Exception e){ System.out.println("/nThere was an error try again./n"); ret = GetMyInt(); } return ret; }


La respuesta dada por Jon Skeet está bien, pero no me gusta devolver un objeto entero null . Encuentro esto confuso de usar. Desde Java 8 hay una mejor opción (en mi opinión), usando el OptionalInt :

public static OptionalInt tryParse(String value) { try { return OptionalInt.of(Integer.parseInt(value)); } catch (NumberFormatException e) { return OptionalInt.empty(); } }

Esto hace que sea explícito que debe manejar el caso donde no hay ningún valor disponible. Preferiría que este tipo de función se agregue a la biblioteca de Java en el futuro, pero no sé si eso sucederá alguna vez.


Me gustaría incluir otra propuesta que funcione si uno solicita enteros específicamente: simplemente use long y use Long.MIN_VALUE para casos de error. Esto es similar al enfoque que se usa para los caracteres en Reader, donde Reader.read () devuelve un entero en el rango de un char o -1 si el lector está vacío.

Para Float y Double, NaN se puede usar de forma similar.

public static long parseInteger(String s) { try { return Integer.parseInt(s); } catch (NumberFormatException e) { return Long.MIN_VALUE; } } // ... long l = parseInteger("ABC"); if (l == Long.MIN_VALUE) { // ... error } else { int i = (int) l; }


Mi Java está un poco oxidada, pero déjame ver si puedo orientarte en la dirección correcta:

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; } }

Si su valor de retorno es null , tiene un valor incorrecto. De lo contrario, tienes un Integer válido.


Para evitar el manejo de excepciones, use una expresión regular para asegurarse de tener primero todos los dígitos:

if(value.matches("//d+") { Integer.parseInt(value); }


Para evitar una excepción, puede usar el método Format.parseObject de Java. El código siguiente es básicamente una versión simplificada de la clase IntegerValidator de Apache Common.

public static boolean tryParse(String s, int[] result) { NumberFormat format = NumberFormat.getIntegerInstance(); ParsePosition position = new ParsePosition(0); Object parsedValue = format.parseObject(s, position); if (position.getErrorIndex() > -1) { return false; } if (position.getIndex() < s.length()) { return false; } result[0] = ((Long) parsedValue).intValue(); return true; }

Puede usar AtomicInteger o el truco de matriz int[] dependiendo de su preferencia.

Aquí está mi prueba que lo usa:

int[] i = new int[1]; Assert.assertTrue(IntUtils.tryParse("123", i)); Assert.assertEquals(123, i[0]);


Podría devolver un Integer lugar de un int , devolviendo null en el fallo de análisis.

Es una pena que Java no proporcione una forma de hacerlo sin que haya una excepción lanzada internamente: puede ocultar la excepción (atrapándola y devolviéndola nula), pero aún podría ser un problema de rendimiento si está analizando cientos de miles de bits de datos proporcionados por el usuario.

EDITAR: Código para tal método:

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

Tenga en cuenta que no estoy seguro de lo que va a hacer si el text es nulo. Debería considerar que, si representa un error (es decir, su código puede pasar un valor no válido, pero nunca debe pasar un valor nulo), es apropiado lanzar una excepción; si no representa un error, probablemente debería devolver nulo como lo haría con cualquier otro valor no válido.

Originalmente, esta respuesta usaba el new Integer(String) constructor new Integer(String) ; ahora usa Integer.parseInt y una operación de boxeo; de esta forma, los valores pequeños terminarán Integer objetos Integer caché, haciéndolo más eficiente en esas situaciones.


Podrías enrollar el tuyo, pero es igual de fácil usar el method StringUtils.isNumeric() commons lang. Utiliza Character.isDigit() para iterar sobre cada carácter en la cadena.


Pruebe con expresión regular y argumento de parámetros por defecto

public static int parseIntWithDefault(String str, int defaultInt) { return str.matches("-?//d+") ? Integer.parseInt(str) : defaultInt; } int testId = parseIntWithDefault("1001", 0); System.out.print(testId); // 1001 int testId = parseIntWithDefault("test1001", 0); System.out.print(testId); // 1001 int testId = parseIntWithDefault("-1001", 0); System.out.print(testId); // -1001 int testId = parseIntWithDefault("test", 0); System.out.print(testId); // 0

si está utilizando apache.commons.lang3 y luego usando NumberUtils :

int testId = NumberUtils.toInt("test", 0); System.out.print(testId); // 0


Puede ser que puedas usar algo como esto:

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; } }


Puedes usar un objeto nulo como ese:

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)); } }

El resultado de main en este ejemplo es:

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

De esta forma, siempre puede probar la conversión fallida pero aún trabajar con los resultados como instancias Integer. Es posible que también desee ajustar el número que NULL representa (≠ 0).


Sugeriría que consideres un método como

IntegerUtilities.isValidInteger(String s)

que luego implementará como mejor le parezca. Si desea que el resultado se transmita, tal vez porque usa Integer.parseInt () de todos modos, puede usar el truco de la matriz.

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

donde establece el resultado [0] en el valor entero encontrado en el proceso.


También estaba teniendo el mismo problema. Este es un método que escribí para pedir al usuario una entrada y no aceptar la entrada a menos que sea un número entero. Tenga en cuenta que soy un principiante así que si el código no funciona como se espera, ¡culpe a mi inexperiencia!

private int numberValue(String value, boolean val) throws IOException { //prints the value passed by the code implementer System.out.println(value); //returns 0 is val is passed as false Object num = 0; while (val) { num = br.readLine(); try { Integer numVal = Integer.parseInt((String) num); if (numVal instanceof Integer) { val = false; num = numVal; } } catch (Exception e) { System.out.println("Error. Please input a valid number :-"); } } return ((Integer) num).intValue(); }


También podría replicar el comportamiento de C ++ que desea de forma muy simple

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; } }

Utilizarías el método así:

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

Después de eso, el result la variable es verdadero si todo salió bien y en byRef[0] contiene el valor analizado.

Personalmente, me quedaría con la excepción.