Una pregunta bastante estúpida. Dado el código:
public static int sum(String a, String b) /* throws? WHAT? */ {
int x = Integer.parseInt(a); // throws NumberFormatException
int y = Integer.parseInt(b); // throws NumberFormatException
return x + y;
}
¿Podrías decir si es bueno Java o no? De lo que estoy hablando NumberFormatException
es de una excepción sin marcar. No tiene que especificarlo como parte de la sum()
firma. Además, según tengo entendido, la idea de las excepciones no verificadas es solo para indicar que la implementación del programa es incorrecta, y aún más, detectar excepciones no verificadas es una mala idea, ya que es como arreglar un programa defectuoso en tiempo de ejecución .
¿Alguien podría aclarar si:
- Debo especificar
NumberFormatException
como parte de la firma del método. - Debo definir mi propia excepción marcada (
BadDataException
), manejarNumberFormatException
dentro del método y volver a lanzarlo comoBadDataException
. - Debo definir mi propia excepción marcada (
BadDataException
), validar ambas cadenas de alguna manera como expresiones regulares y lanzar miBadDataException
si no coincide. - ¿Tu idea?
Actualización :
Imagínese, no es un marco de código abierto, que debería usar por alguna razón. Miras la firma del método y piensas: "Está bien, nunca arroja". Entonces, algún día, tienes una excepción. ¿Es normal?
Actualización 2 :
Hay algunos comentarios que dicen que mi sum(String, String)
es un mal diseño. Estoy absolutamente de acuerdo, pero para aquellos que creen que el problema original nunca aparecería si tuviéramos un buen diseño, aquí hay una pregunta adicional:
La definición del problema es la siguiente: tiene una fuente de datos donde los números se almacenan como String
s. Esta fuente puede ser un archivo XML, una página web, una ventana de escritorio con 2 cuadros de edición, lo que sea.
Su objetivo es implementar la lógica que toma estos 2 String
s, los convierte en int
sy muestra un cuadro de mensaje que dice "la suma es xxx".
No importa cuál sea el enfoque que utilice para diseñar / implementar esto, tendrá estos 2 puntos de funcionalidad interna :
- Un lugar donde se convierte
String
aint
- Un lugar donde agregas 2
int
s
La pregunta principal de mi publicación original es:
Integer.parseInt()
espera que se pase la cadena correcta . Siempre que pase una cadena incorrecta , significa que su programa es incorrecto (no " su usuario es un idiota"). Debe implementar el fragmento de código donde, por un lado, tiene Integer.parseInt () con semántica MUST y, por otro lado, debe estar de acuerdo con los casos en los que la entrada es incorrecta: DEBERÍA semántica .
Entonces, brevemente: ¿cómo implemento la semántica DEBERÍA si solo tengo bibliotecas MUST ?
Respuestas:
Esta es una buena pregunta. Ojalá más gente pensara en esas cosas.
En mi humilde opinión, lanzar excepciones no marcadas es aceptable si se le han pasado parámetros de basura.
En términos generales, no debe lanzar
BadDataException
porque no debe usar Excepciones para controlar el flujo del programa. Las excepciones son para los excepcionales. Las personas que llaman a su método pueden saber antes de llamarlo si sus cadenas son números o no, por lo que pasar basura es evitable y, por lo tanto, puede considerarse un error de programación , lo que significa que está bien lanzar excepciones sin marcar.Con respecto a la declaración
throws NumberFormatException
, esto no es tan útil, porque pocos lo notarán debido a que NumberFormatException está desmarcado. Sin embargo, los IDE pueden hacer uso de él y ofrecerse a integrarsetry/catch
correctamente. Una buena opción es usar javadoc también, por ejemplo:/** * Adds two string numbers * @param a * @param b * @return * @throws NumberFormatException if either of a or b is not an integer */ public static int sum(String a, String b) throws NumberFormatException { int x = Integer.parseInt(a); int y = Integer.parseInt(b); return x + y; }
EDITADO :
Los comentaristas han hecho puntos válidos. Debe considerar cómo se utilizará y el diseño general de su aplicación.
Si el método se usará en todas partes, y es importante que todas las personas que llaman manejen los problemas, declare que el método arroja una excepción marcada (obligando a las personas que llaman a lidiar con los problemas), pero saturando el código con
try/catch
bloques.Si, por otro lado, estamos usando este método con datos en los que confiamos, entonces declárelo como se indicó anteriormente, porque no se espera que explote nunca y evitará el desorden de código de
try/catch
bloques esencialmente innecesarios .fuente
RuntimeException
manejar? ¿Debería ser la persona que llama o debería dejarse flotar la excepción? Si es así, ¿cómo se debe manejar?Exceptions
desde capas inferiores y envolverlas en Excepciones de nivel superior que son más significativas para el usuario final y el segundo es usar un controlador de excepciones no detectado que se puede inicializar en el lanzador de aplicaciones.throws
cláusula es una buena adición, pero no es obligatorio.En mi opinión, sería preferible manejar la lógica de excepción lo más arriba posible. Por eso preferiría la firma
public static int sum(int a, int b);
Con la firma de su método no cambiaría nada. O eres
Por lo tanto, el manejo de excepciones en este caso se convierte en un problema de documentación .
fuente
sum
función que espera dos números, debería obtener dos números. La forma en que los obtuvo no está relacionada con lasum
función. Separe su lógica correctamente . Entonces, relacionaría esto con un problema de diseño.Número 4. Como se indica, este método no debe tomar cadenas como parámetros, debe tomar números enteros. En cuyo caso (dado que Java se ajusta en lugar de desbordarse) no hay posibilidad de una excepción.
es mucho más claro en cuanto a lo que se quiere decir que
x = sum(a, b)
Quiere que la excepción ocurra lo más cerca posible de la fuente (entrada).
En cuanto a las opciones 1-3, no define una excepción porque espera que las personas que llaman asuman que, de lo contrario, su código no puede fallar, define una excepción para definir lo que sucede en condiciones de falla conocidas QUE SON ÚNICAS PARA SU MÉTODO. Es decir, si tiene un método que es un envoltorio alrededor de otro objeto y arroja una excepción, páselo. Solo si la excepción es única para su método, debe lanzar una excepción personalizada (frex, en su ejemplo, si se suponía que sum solo devolviera resultados positivos, entonces sería apropiado verificar eso y lanzar una excepción, si por otro lado java arrojó una excepción de desbordamiento en lugar de envolver, luego pasaría eso, no lo definiría en su firma, lo renombraría o lo comería).
Actualización en respuesta a la actualización de la pregunta:
La solución a esto es envolver la biblioteca MUST y devolver un valor DEBERÍA. En este caso, una función que devuelve un entero. Escriba una función que tome una cadena y devuelva un objeto Integer, o funciona o devuelve nulo (como Ints.tryParse de guava). Haga su validación por separado de su operación, su operación debe tomar en cuenta. Si se llama a su operación con valores predeterminados cuando tiene una entrada no válida, o si hace otra cosa, dependerá de sus especificaciones; lo más que puedo decir al respecto es que es realmente poco probable que el lugar para tomar esa decisión sea en la operación método.
fuente
sum(int, int)
yparseIntFromString(String)
. Para obtener la misma funcionalidad, en cualquier caso, tendré que escribir un fragmento de código donde hay 2 llamadas aparseIntFromString()
y 1 llamada asum()
. Considere este caso, si tiene sentido para usted, no veo ninguna diferencia desde el punto de vista del problema original.int sum(String, String)
nunca es posible en realidad?Creo que sí. Es una buena documentación.
A veces sí. Las excepciones marcadas se consideran mejores en algunos casos, pero trabajar con ellas es todo un PITA. Es por eso que muchos frameworks (por ejemplo, Hibernate) usan solo excepciones en tiempo de ejecución.
Nunca. Más trabajo, menos velocidad (a menos que espere que lanzar la excepción sea una regla) y ninguna ganancia en absoluto.
Ninguno en absoluto.
fuente
No 4.
Creo que no cambiaría el método en absoluto. Pondría un intento de captura alrededor del método de llamada o superior en el seguimiento de la pila donde estoy en un contexto en el que puedo recuperarme elegantemente con la lógica empresarial de la excepción.
No haría con certeza el número 3 ya que lo considero exagerado.
fuente
Suponiendo que lo que está escribiendo va a ser consumido (como una API) por otra persona, entonces debe ir con 1, NumberFormatException es específicamente con el propósito de comunicar tales excepciones y debe usarse.
fuente
Primero debe preguntarse si el usuario de mi método debe preocuparse por ingresar datos incorrectos o si se espera que ingrese los datos correctos (en este caso, String). Esta expectativa también se conoce como diseño por contrato .
y 3. Sí, probablemente debería definir BadDataException o incluso mejor usar algunos de los que eliminan como NumberFormatException, sino dejar que se muestre el mensaje estándar. Capture NumberFormatException en el método y vuelva a lanzarlo con su mensaje, sin olvidar incluir el seguimiento de pila original.
Depende de la situación, pero probablemente volvería a lanzar NumberFormatException con información adicional. Y también debe haber una explicación javadoc de cuáles son los valores esperados para
String a, String b
fuente
Depende mucho del escenario en el que te encuentres.
Caso 1. Siempre eres tú quien depura el código y nadie más y la excepción no causará una mala experiencia de usuario
Lanzar la NumberFormatException predeterminada
Caso 2: el código debe ser extremadamente fácil de mantener y comprensible
Defina su propia excepción y agregue muchos más datos para depurar mientras la lanza.
No necesita verificaciones de expresiones regulares ya que, de todos modos, irá a la excepción en caso de entrada incorrecta.
Si fuera un código de nivel de producción, mi idea sería definir más de una excepción personalizada, como
y lidiar con todos estos por separado
fuente
En general, una NumberFormaException no está marcada porque se espera que se proporcione una entrada que se pueda analizar correctamente. La validación de entrada es algo que debe manejar. Sin embargo, analizar la entrada es la forma más sencilla de hacer esto. Simplemente puede dejar su método como está y advertir en la documentación que se espera una entrada correcta y cualquier persona que llame a su función debe validar ambas entradas antes de usarla.
fuente
Cualquier comportamiento excepcional debe aclararse en la documentación. O debe indicar que este método devuelve un valor especial en caso de error (como
null
, cambiando el tipo de retorno aInteger
) o debe usarse el caso 1. Tenerlo explícito en la firma del método permite al usuario ignorarlo si asegura cadenas correctas por otros medios, pero aún es obvio que el método no maneja este tipo de falla por sí mismo.fuente
Responda a su pregunta actualizada.
Sí, es perfectamente normal tener excepciones "sorpresa". Piense en todos los errores de tiempo de ejecución que uno tiene cuando es nuevo en la programación.
También es una excepción sorpresa común para cada bucle.
fuente
Si bien estoy de acuerdo con la respuesta de que se debe permitir filtrar la excepción de tiempo de ejecución, desde una perspectiva de diseño y usabilidad, sería una buena idea envolverla en una IllegalArgumentException en lugar de lanzarla como NumberFormatException. Esto luego hace que el contrato de su método sea más claro mediante el cual declara que se le pasó un argumento ilegal debido al cual lanzó una excepción.
Con respecto a la actualización de la pregunta "Imagina, no es un marco de código abierto, que deberías usar por alguna razón. Miras la firma del método y piensas:" Está bien, nunca arroja ". Luego, algún día, obtuviste una excepción . ¿Es normal?" el javadoc de su método siempre debe derramar el comportamiento de su método (restricciones previas y posteriores). Piense en las líneas de, por ejemplo, las interfaces de colección, donde si no se permite un nulo, el javadoc dice que se lanzará una excepción de puntero nulo, aunque nunca es parte de la firma del método.
fuente
Como estás hablando de una buena práctica de Java, en mi opinión, siempre es mejor
Para manejar la excepción no marcada, analícela y a través de una excepción personalizada sin marcar.
Además, mientras lanza una excepción personalizada sin marcar, puede agregar el mensaje de excepción que su cliente podría entender y también imprimir el seguimiento de la pila de la excepción original
No es necesario declarar la excepción personalizada como "lanzamientos", ya que no está marcada.
De esta manera, no está violando el uso de las excepciones no verificadas, al mismo tiempo que el cliente del código entendería fácilmente el motivo y la solución de la excepción.
Además, documentar correctamente en java-doc es una buena práctica y ayuda mucho.
fuente
Creo que depende de tu propósito, pero lo documentaría como mínimo:
/** * @return the sum (as an int) of the two strings * @throws NumberFormatException if either string can't be converted to an Integer */ public static int sum(String a, String b) int x = Integer.parseInt(a); int y = Integer.parseInt(b); return x + y; }
O tome una página del código fuente de Java para la clase java.lang.Integer:
public static int parseInt(java.lang.String string) throws java.lang.NumberFormatException;
fuente
¿Qué tal el patrón de validación de entrada implementado por la biblioteca 'Guava' de Google o la biblioteca 'Validator' de Apache ( comparación )?
En mi experiencia, se considera una buena práctica validar los parámetros de una función al comienzo de la función y lanzar Excepciones cuando corresponda.
Además, consideraría que esta pregunta es en gran medida independiente del idioma. La 'buena práctica' aquí se aplicaría a todos los lenguajes que tienen funciones que pueden tomar parámetros que pueden o no ser válidos.
fuente
Creo que su primera frase de "Una pregunta bastante estúpida" es muy relevante. ¿Por qué escribirías un método con esa firma en primer lugar? ¿Tiene sentido sumar dos cadenas? Si el método de llamada quiere sumar dos cadenas, es responsabilidad del método de llamada asegurarse de que sean entradas válidas y convertirlas antes de llamar al método.
En este ejemplo, si el método de llamada no puede convertir las dos cadenas en un int, podría hacer varias cosas. Realmente depende en qué capa se produce esta suma. Supongo que la conversión de cadenas estaría muy cerca del código de front-end (si se hizo correctamente), de modo que el caso 1 sería el más probable:
fuente
Hay muchas respuestas interesantes a esta pregunta. Pero todavía quiero agregar esto:
Para el análisis de cadenas, siempre prefiero usar "expresiones regulares". El paquete java.util.regex está ahí para ayudarnos. Así que terminaré con algo como esto, que nunca arroja ninguna excepción. Depende de mí devolver un valor especial si quiero detectar algún error:
import java.util.regex.Pattern; import java.util.regex.Matcher; public static int sum(String a, String b) { final String REGEX = "\\d"; // a single digit Pattern pattern = Pattern.compile(REGEX); Matcher matcher = pattern.matcher(a); if (matcher.find()) { x = Integer.matcher.group(); } Matcher matcher = pattern.matcher(b); if (matcher.find()) { y = Integer.matcher.group(); } return x + y; }
Como se puede ver, el código es un poco más largo, pero podemos manejar lo que queremos (y establecer valores predeterminados para xey, controlar lo que sucede con las cláusulas else, etc.) Incluso podríamos escribir una transformación más general rutina, a la que podemos pasar cadenas, valores de retorno predeterminados, código REGEX para compilar, mensajes de error para lanzar, ...
Espero que haya sido útil.
Advertencia: no pude probar este código, así que disculpe posibles problemas de sintaxis.
fuente
Integer.matcher
?private
variable dentro del método? Missing()
Para el IFS, falta mucha;
,x
,y
no declarada,matcher
declarado dos veces, ...Integer.matcher.group()
sea asíInteger.parseInt(matcher.group())
)Se enfrenta a este problema porque permite que los errores de usuario se propaguen demasiado profundamente en el núcleo de la aplicación y, en parte, también porque abusa de los tipos de datos de Java.
Debería tener una separación más clara entre la validación de entrada del usuario y la lógica empresarial, utilizar la escritura de datos adecuada y este problema desaparecerá por sí solo.
El hecho es que la semántica de
Integer.parseInt()
es conocida: su propósito principal es analizar números enteros válidos . Falta un paso de análisis / validación de entrada de usuario explícito.fuente