Este capítulo analiza varios aspectos del diseño de métodos: cómo manejar parámetros y valores de retorno, cómo diseñar firmas de métodos y cómo documentar métodos. La mayor parte de este capítulo se aplica tanto a los constructores como a los métodos ordinarios. Al igual que el Capítulo 4, el enfoque de este capítulo está en la usabilidad, la solidez y la flexibilidad.
La mayoría de los métodos y constructores tienen ciertas restricciones en los valores de los parámetros que se les pasan. Por ejemplo, el valor del índice no debe ser negativo, la referencia del objeto no puede ser nula, etc. Todos estos son muy comunes. Debe indicar claramente estas restricciones en la documentación y hacerlas cumplir verificando los parámetros al comienzo del cuerpo del método. Es un caso especial del principio general de que los errores deben detectarse lo antes posible después de que se produzcan. Si esto no se puede hacer, es menos probable que se detecte el error e incluso si se detecta un error, es más difícil determinar el origen del error.
Si pasa valores de parámetros no válidos a un método que verifica los parámetros antes de ejecutarse, fallará rápida y claramente con una excepción apropiada. Si este método no comprueba sus parámetros pueden pasar varias cosas. El método puede fallar durante el procesamiento y producir una excepción desconcertante. Peor aún, el método regresa normalmente pero silenciosamente calcula el resultado incorrecto. Lo peor es que el método puede regresar normalmente, pero deja un objeto en un estado destruido, lo que provocará un error en un momento no relacionado en el futuro en un momento incierto. En otras palabras, no verificar la validez de los parámetros puede provocar una violación de la atomicidad del fallo; consulte el punto 76 para obtener más detalles.
Para métodos públicos y protegidos, use la etiqueta Javadoc @throws para documentar las excepciones que se generarán cuando se violen las restricciones de valor de parámetro. Estas excepciones suelen ser IllegalArgumentException, IndexOutOfBoundsException o NullPointerException (consulte el elemento 72 para obtener más detalles). Una vez que se documentan las restricciones sobre los parámetros del método y las excepciones que se generarán si se violan estas restricciones, es sencillo hacer cumplir estas restricciones. A continuación se muestra un ejemplo típico:
Tenga en cuenta que el comentario de la documentación no dice que se genera una excepción NullPointerException si m es nulo, sino que es un subproducto de llamar a m.signum(), aunque el método sí lo hace. exactamente eso de. La documentación para la excepción NullPointerException se basa en los comentarios de la documentación a nivel de clase de la clase BigInteger circundante. Las anotaciones a nivel de clase se aplican a todos los parámetros de todos los métodos públicos de la clase. Esto puede evitar la confusión causada al documentar cada NullPointerException en cada método por separado. Se puede utilizar junto con @Nullable o anotaciones similares para indicar que un parámetro especial puede ser nulo, pero esta práctica no es estándar y existen varias anotaciones que pueden realizar esta función.
El método Objects.requireNonNull agregado en Java7 es más flexible y conveniente, por lo que no es necesario verificar manualmente si hay nulos. Si lo desea, también puede especificar sus propios detalles de excepción. Este método devuelve su entrada, por lo que puede usar un valor mientras realiza una verificación nula:
También puede ignorar el valor devuelto y usar Objects.requireNonNull como una verificación nula independiente cuando sea necesario.
En Java 9, se agregó una función para verificar el alcance: java.util.Objects. Esta función contiene tres métodos: checkFromIndexSize, checkFromToIndex y checkIndex.
Esta función no es tan flexible como el método de verificación de nulos. No le permite especificar sus propios detalles de excepción, pero está diseñado específicamente para usarse con índices de listas y matrices. No maneja rangos cerrados (incluidos sus dos puntos finales). Pero si hace exactamente lo que necesitas, entonces es una herramienta útil.
Para los métodos no exportados, como creador del paquete, usted tiene control sobre las circunstancias bajo las cuales se llamará este método, por lo que puede y debe asegurarse de que solo se pasen valores de parámetros válidos. Por lo tanto, los métodos no públicos generalmente deberían usar aserciones para verificar sus parámetros, de la siguiente manera:
Esencialmente, estas aserciones afirman que la condición afirmada será verdadera, aburriendo a los clientes de paquetes periféricos sobre cómo usarlas. A diferencia de las comprobaciones de validez generales, si una afirmación falla, se generará un AssertionError. A diferencia de las comprobaciones de validez normales, esencialmente no hay ningún costo si no funcionan a menos que se habiliten pasando el indicador -ea (o -enableassertions) al intérprete de Java. Para obtener más información sobre las afirmaciones, consulte el tutorial de Sun.
Para algunos parámetros, el método en sí no se utiliza, sino que se guarda para su uso posterior. Es particularmente importante comprobar la validez de dichos parámetros. Por ejemplo, tome el método de fábrica estático en el elemento 20 como ejemplo. Su parámetro es una matriz int y devuelve una vista de Lista de la matriz. Si el cliente de este método pasara nulo, el método arrojaría una NullPointerException porque el método contiene una verificación de condición explícita (llamando a Objects.requireNonNull). Si se omite esta verificación condicional, devolverá una referencia a la instancia de Lista recién creada. Una vez que el cliente intente utilizar esta referencia, se generará una NullPointerException inmediatamente. En ese punto, puede resultar muy difícil encontrar el origen de la instancia de Lista, lo que complica la depuración.
Como se mencionó anteriormente, algunos parámetros se guardan mediante métodos para su uso posterior, y el constructor representa un caso especial de este principio. Es muy importante comprobar la validez de los parámetros del constructor para evitar construir objetos que violen las restricciones de esta clase.
Existen excepciones a la regla de que un método debe verificar sus parámetros antes de realizar sus cálculos. Una excepción importante es que en algunos casos, o simplemente no es práctica, la verificación de validez se realiza implícitamente en el proceso de cálculo. Por ejemplo, tomando el método Collections.sort(List) para ordenar una lista de objetos, todos los objetos de la lista deben ser comparables entre sí. Durante el proceso de ordenar una lista, cada objeto de la lista se compara con algún otro objeto. Si estos objetos no son comparables entre sí, una de las operaciones de comparación generará una ClassException, que es exactamente lo que se supone que debe hacer el método de clasificación. Por tanto, no tiene mucho sentido comprobar de antemano si los elementos de la lista son comparables entre sí. Sin embargo, tenga en cuenta que el uso indiscriminado de este método dará como resultado la pérdida de atomicidad del fallo; consulte el elemento 76 para obtener más detalles.
A veces, ciertos cálculos realizarán implícitamente las comprobaciones de validez necesarias, pero si las comprobaciones no tienen éxito, se generará una excepción incorrecta. En otras palabras, la excepción lanzada por el proceso de cálculo debido a valores de parámetros no válidos no coincide con la excepción que la documentación indica que arrojará este método. En este caso, se debe utilizar la técnica de conversión de excepciones descrita en el elemento 73 para convertir la excepción lanzada durante el cálculo en la excepción correcta.
No concluya del contenido de este artículo que cualquier restricción en los parámetros es algo bueno. En cambio, al diseñar métodos, debe hacerlos lo más generales posible y coherentes con las necesidades prácticas. Si un método puede hacer un trabajo razonable para todos los valores de parámetros que acepta, la cantidad de parámetros debe ser la menor posible.
Sin embargo, normalmente existen limitaciones inherentes a la abstracción que se implementa.
En resumen, siempre que escribas un método o constructor, debes considerar las restricciones en sus parámetros. Estas restricciones deben documentarse y aplicarse mediante comprobaciones explícitas al comienzo del cuerpo del método. Es muy importante desarrollar este hábito. Siempre que la verificación de validez falle una vez, los esfuerzos que realice para realizar las comprobaciones de validez necesarias se reembolsarán con intereses.