2010-07-16 15 views
6

¿Le lanzar una IllegalStateException si:¿IllegalStateException es apropiado para un objeto inmutable?

  1. Un método es incapaz de hacer su trabajo debido al valor (s) de uno o más campos
  2. Esos campos están final y asigna únicamente en el constructor?

ejemplo clásico: su clase es una inmutable Collection<BigInteger> y su método se supone que devuelve el elemento máximo, pero esta instancia está vacía.

He leído Kevin Bourillon`s blog post sobre el tema y no estoy seguro de qué regla se aplica.

UnsupportedOperationException: esto significa que el método invocado siempre fallará para una instancia de esta clase (tipo concreto), independientemente de cómo se haya construido la instancia.

Definitivamente no. Muchas instancias de esta clase no están vacías y la operación habría tenido éxito.

IllegalStateException - ... sí existe al menos un estado alternativo que la instancia en cuestión podría haber estado en, lo que habría superado el control ... <tijeretazo> ... Tenga en cuenta también que esta excepción es apropiado si es posible o no mutar realmente este aspecto del estado de la instancia, o si ya es demasiado tarde.

No del todo. Esta instancia se construyó con longitud cero, por lo que esta instancia no es y nunca podría haber estado vacía.

IllegalArgumentException - al lanzar esta excepción, implica que existe al menos otro valor para este parámetro que habría provocado que la verificación en cuestión pase.

Podría aplicarse si el parámetro en cuestión es el parámetro this implícito. Esta es la excepción que estoy tentado a arrojar, pero me preocupa que pueda ser confuso.


actualización: ejemplo cambió Collection<Integer>-Collection<BigInteger> debido al hecho de que había un elemento de identidad (Integer.MIN_VALUE) distrae de la cuestión.

+0

Hay dos cosas diferentes en esta pregunta. ** Uno ** es tener un método de utilidad, que devuelve el máximo de una colección determinada (que, como apunta Jon, es exactamente como el método Collections.max) El ** otro ** está en el título de su pregunta, un * objeto inmutable * tener un método máximo, que devuelve el valor máximo de un objeto delegado o el propio.Este segundo escenario es el que estoy respondiendo http://stackoverflow.com/questions/3267958/is-illegalstateexception-approved-for-an-immutable-object/3268353#3268353 – OscarRyz

Respuesta

4

Creo que IllegalStateException es apropiado aquí. La instancia podría haber estado en el estado correcto, si se construyó correctamente (es decir, la parte "ya es demasiado tarde").

+2

En ese caso, debería ser una excepción de argumento ilegal, pero ** cuando ** se da cuenta de que el objeto se está construyendo incorrectamente. – OscarRyz

+1

@OscarRyz Eso solo es cierto si ninguno de sus métodos puede manejar el estado dado. Si getMax() es la única función para la cual el estado es ilegal, yo diría que IllegalStateException es apropiado. – ILMTitan

+2

ILM Mhh, tienes razón al respecto, pero en ese caso prefiero usar 'UnsupportedOperationException', pero eso es complicado ... mmmhh qué tal ...' UnsopporteOperationCausedByIllegalStateOriginatedByIllegalArgumentException' :) – OscarRyz

11

No parece que ninguna de las clases de excepciones comunes que mencione anteriormente se ajusten al ejemplo del libro de texto.

Debe arrojar un NoSuchElementException ya que es exactamente lo que hace el método Collections.max().

+3

+1 para encontrar algo de "estado de la técnica". –

+0

Generalmente no me gusta usar valores de excepción como '-1', pero en este caso, * es * un elemento de identidad:' Integer.MIN_VALUE'. Eso solo muestra que escogí un mal ejemplo, así que voy a editar la pregunta para referirme a 'BigInteger' en lugar de' Integer' :-) – finnw

+5

+1 a NoSuchElementException, pero el 'return -1' es una mala idea. Por lo tanto, devolver 'Integer.MIN_VALUE', que si se devuelve todavía no le dirá si realmente hay un valor máximo o no (¿qué ocurre con una colección que contiene solo Integer.MIN_VALUE? Null es una mejor opción que ambas, pero no tan buena como excepción IMO. –

3

Si el estado de la clase es válido (colección vacía), el elemento máximo es simplemente nulo.Si el estado no es válido, se debería haber lanzado una excepción IllegalArgumentException en el momento de la construcción.

+0

+1 exactamente. .. – OscarRyz

3

IllegalStateException es lo más parecido a lo que desea: "esta excepción es adecuada independientemente de si es posible o no mutar este aspecto del estado de la instancia".

Not UnsupportedOperationException, ya que podría tener éxito para alguna instancia de esa clase, y lanzar IllegalArgumentException para un método que (presumiblemente) no toma argumentos sin duda confundirá a las personas.

+0

Pero un objeto inmutable tiene solo un estado ...: -/ – OscarRyz

+0

No, solo tiene un estado ACCESIBLE. Ver la segunda causa de mi primera oración. – DJClayworth

2

¿IllegalStateException es apropiado para un objeto inmutable?

No, porque los objetos inmutables solo tienen un estado y no podrían pasar de un estado legal a otro.

Por lo tanto, usted está construyendo un objeto inmutable, y su objeto debe tener un método máx

class YourObject { 
    public BigInteger max(){ ... } 
} 

que este caso IllegalAgumentException debería ser la correcta, pero no hasta que se ejecuta el método, pero cuando el ¡se crea el objeto!

Por lo tanto, en este escenario, si tiene una colección inmutable de bigtegers y la crea con cero elementos, recibe un "argumento inválido" en la creación de la colección, cuando debe lanzar la excepción.

Estoy de acuerdo con Jon, si su caso de uso, o en su análisis, está dispuesto a apoyar el resto de las operaciones, podría lanzar NoSuchElementException, pero creo que eso sería posponer el problema. Sería mejor evitar la creación de objetos en primer lugar.

Así, lanzando IllegalArgumentException sería como:

// this is my immutable object class 
    final class YourObject { 
     private final Collection<BigInteger> c; 
     public YourObject(BigInteger ... values) { 
      if(values.length == 0) { 
       throw new IllegalAgumentException("Must specify at least one value"); 
      } 
      ... initialize the rest... 
     } 
     public BigInteger max() { 
      // find and return the max will always work 
     } 
    } 

Cliente:

YourObject o = new YourObject(); // throws IllegalArgumentException 
    // the rest is not executed.... 
    doSomething(o) ; 
    ... 
    doSomething(YourObject o) { 
     BigInteger maximum = o.max(); 
    } 

En este caso no es necesario para comprobar si hay cualquier cosa en doSomething porque el programa podría fallar en la instancia creación, que a su vez se fijaría en el momento del desarrollo.

Lanzar NoSuchElementException sería como:

final class YourObject { 
     private final Collection<BigInteger> c; 
     public YourObject(BigInteger ... values) { 
      // not validating the input :-/ oh oh.. 
      ... initialize the rest... 
     } 
     public BigInteger max() { 
      if(c.isEmpty()) { throw NoSuchElementException(); } 
      // find and return the max will always work after this line 
     } 
    } 

Cliente:

YourObject o = new YourObject(); // it says nothing 
    doSomething(o) ; 
    ... 
    doSomething(YourObject o) { 
     BigInteger maximum = o.max();// ooops!, why? what?... 
     // your object client will start questioning what did I do wrong 
     // and chais of if(o != null && o.isEmpty() || moonPhaseIs...) 
    } 

Tenga en cuenta que, si un programa es a fallar, lo mejor que puede hacer es to making it fail fast.

Collections.max tienen un propósito diferente, porque al ser un método de utilidad (no un objeto inmutable), no puede responsabilizarse por la creación de la colección vacía (no estaba presente cuando eso sucedió), lo único que puede hacer es diga "No hay tal cosa como max en esta colección" por lo tanto, NoSuchElementException.

Una última observación, RuntimeExceptions, should be used for programming mistakes only (aquellos que pueden ser fijado por probar la aplicación antes de soltarlo)

2

Usted debe arrojado un UnsupportedOpertationException porque eso es lo que hace la biblioteca estándar de Java en la misma circunstancia. Su ejemplo es un protocolo de objeto calificador tipo. Este patrón se define en "An Empirical Study of Object Protocols in the Wild":

Algunos tipos desactivar determinados métodos de la vida útil de el objeto. En la categoría de calificador de tipo, una instancia de objeto ingresará un estado abstracto S en el tiempo de construcción que nunca abandonará. Las llamadas a una instancia método m, si está deshabilitada en el estado S siempre fallarán.

En su ejemplo, su objeto entra en un estado abstracto que voy a llamar EmptyCollection en el momento de la construcción y que nunca deja ese estado debido a que el campo de recolección es final. En el estado abstracto emptyCollection, todas las llamadas al método de instancia getMax() siempre fallarán.

Beckman estudió programas Java de código abierto en busca de protocolos de objetos y categorizó las clases resultantes. La tercera categoría de protocolo más común, que aparece en el 16.4% de los protocolos muestreados, fue calificador de tipo.

listas de papel de Beckman muchos ejemplos de tipo de clasificación y me seleccionaron tres de ellos y en cada caso el método no está disponible, se emite una UnsupportedOperationException:

  1. Al crear una lista no modificable llamando Colections.unmodifiableList(...) y luego llamar al método add en la lista resultante.
  2. Cuando crea un java.nio.ByteBuffer que no está respaldado por una matriz y luego llama al método array.
  3. Cuando crea un java.imageio.ImageWriteParam que no es compatible con la compresión y luego llama al método setCompressionMode.

en cuenta que estos ejemplos hacen no seguir el consejo de Kevin Bourillon que usted cita. El fracaso de estos métodos depende de cómo se construyeron las instancias. En los ejemplos 1 y 2, las instancias que tienen éxito y fallan pueden ser de clases concretas diferentes ya que List y ByteBuffer son abstractos. Sin embargo, ImageWriteParam es una clase concreta, por lo que una instancia de ImageWriteParam puede arrojar el UnsupportedOperationException, mientras que la otra no. Como los diseñadores de la Biblioteca Estándar de Java también definieron los tipos de excepción, yo seguiría su ejemplo en lugar del del Sr. Bourillon.

P.S. Debería usar IllegalStateException cuando en su lugar el estado abstracto de su objeto puede cambiar en el tiempo de ejecución. El otro 83.6% de los ejemplos en el documento de Beckman son de este tipo.

Cuestiones relacionadas