2012-01-21 19 views
11

Como siempre lo he entendido, los principales casos en que un instanceof es apropiado son:¿Cuándo se usa una instancia de la decisión correcta?

  1. La implementación de Object.equals(Object). Entonces, si estuviera escribiendo una clase List y no extendiera AbstractList por ningún motivo, implementaría equals(o) probando primero o instanceof List, y luego comparando elementos.
  2. Una optimización significativa (¿algorítmica?) Para un caso especial que no cambia la semántica, pero solo el rendimiento. Por ejemplo, Collections.binarySearch realiza una prueba instanceof RandomAccess y utiliza una búsqueda binaria ligeramente diferente para las listas RandomAccess y no RandomAccess.

No creo que instanceof represente un olor a código en estos dos casos. ¿Pero hay otros casos donde es sensato usar instanceof?

Respuesta

6

código heredado o API fuera de su control son un uso legítimo -case para instanceof. (Incluso entonces prefiero escribir una capa OO sobre ella, pero el tiempo a veces impide un rediseño como ese.)

En particular, las fábricas basadas en jerarquías de clases externas parecen un uso común.

+0

Hmmmmm. ¿Algún ejemplo en mente? No he encontrado esa necesidad con ninguna API con la que haya jugado. –

+0

@LouisWasserman Cualquier cosa escrita de forma no OO, por ejemplo, teníamos una API de terceros donde varias clases FTP no extendían una clase base común ni implementaban una interfaz. Para tratarlos de la misma manera, nuestra solución rápida fue una instancia trivial de verificación. Finalmente colocamos una capa sobre la parte superior. Hay un montón de malas API en el mundo. –

+0

Ew. Supongo que debería estar agradecido por no haber tenido que lidiar con ninguna de esas bestias. –

-1

Como ha mencionado, los usos "correctos" de instanceof son bastante limitados. Hasta donde yo sé, básicamente has resumido los dos usos principales.

Sin embargo se puede generalizar sus declaraciones un poco a pesar de la siguiente manera:

  1. Tipo de comprobación antes de moldes necesarios.
  2. La implementación de escenarios de casos especiales que dependen de instancias de clases muy particulares
4

Su primer caso es un ejemplo en el que no usaría el operador instanceof, pero ver si las clases son iguales:

o != null && o.getClass() == this.getClass() 

Esto evitará que una instancia de A extends B y B se consideren iguales

Otros casos en los que puedo pensar inmediatamente, pero estoy bastante seguro de que más v Alid casos están disponibles

  • casos fábrica donde se tiene por ejemplo un método canCreate y create que reciben una interfaz general como parámetro. Cada una de las fábricas puede manejar una implementación específica de la interfaz, por lo que requeriría un instanceof. Definiendo sólo la interfaz en la fábrica abstracta clase/interfaz permite, por ejemplo, para escribir una fábrica compuesta
  • implementaciones compuestos (como se ilustra en mi primer ejemplo)
+1

Pero en el caso 'List', _want_ una instancia de' B extiende A' y 'C extiende A' para que se considere igual. –

+0

Su primer punto es a veces muy importante. Sin embargo, hay aplicaciones en las que desea que una instancia de subclase sea 'igual' a una instancia de clase principal. En ese caso, 'instanceof' tiene más sentido que verificar la identidad de clase. Además, 'instanceof' es equivalente a su verificación de identidad de clase para las clases finales. No he comparado los dos métodos, pero no me sorprendería si 'instanceof' es un poco más rápido en ese caso. –

+0

@LouisWasserman Pero 'instanceof' fallará en ese caso:' anA instanceof aB' será 'false'. –

5

Una forma de responder a su pregunta sería responder "¿cuándo una biblioteca sólida de Java usa instanceof?"Si suponemos que Guava es un ejemplo de una biblioteca Java bien diseñada, podemos ver dónde utiliza instanceof para decidir cuándo es aceptable hacerlo.

Si extraemos el código fuente de la jarra de guayaba y grep, vemos instanceof se menciona 439 veces, a través de 122 archivos:

$ pwd 
/tmp/guava-13.0.1-sources 
$ grep -R 'instanceof' | wc -l 
439 
$ grep -Rl 'instanceof' | wc -l 
122 

Y mirando a algunos de estos casos podemos ver varios patrones emergen:

  • para comprobar la igualdad

    Este es el uso más común. Esto es algo específico de la implementación, pero suponiendo que de hecho desea medir la igualdad en función de la clase/interfaz que amplía/implementa, puede usar instanceof para asegurarse de que el objeto con el que trabaja es. Sin embargo, esto puede causar problemas extraños si una clase secundaria anula equals() y no respeta los mismos requisitos instanceof que el padre. Ejemplos en todas partes, pero uno fácil es Lists.equalsImpl() que es utilizado por ImmutableList.

  • Para cortocircuito construcción de objetos innecesarios

    Puede utilizar instanceof para comprobar si la pasaron en el argumento puede ser utilizado o devuelto de forma segura sin transformar aún más que, por ejemplo, si ya una instancia de la deseada clase, o si sabemos que es inmutable. Véanse los ejemplos en CharMatcher.forPredicate(), Suppliers.memoize(), ImmutableList.copyOf(), etc.

  • Para acceder a los detalles de implementación-sin exponer comportamiento diferente

    Esto puede ser visto por todo el lugar en la guayaba, pero sobre todo en las clases de utilidad en estáticas el paquete com.google.common.collect, por ejemplo en Iterables.size() donde llama a Collection.size() si es posible, y de lo contrario cuenta el número de elementos en el iterador en el tiempo O(n).

  • Para evitar llamar toString()

    Soy escéptico esto merece que se realiza en más de un muy selecto pocos casos, pero suponiendo que esté seguro de que está haciendo lo correcto, se puede evitar la innecesaria Convirtiendo CharSequence objetos en String s con , como se hace en Joiner.toString(Object).

  • Para ello manejo complejo

    Obviamente lo "correcto" que hacer es utilizar un bloque try/catch (aunque en realidad, que está haciendo instanceof cheques ya) Excepción, pero a veces hay lógica de manejo más complejo que merece usar bloques condicionales o pasar el procesamiento a un método separado, por ejemplo, extraer causas o tener un procesamiento específico de la implementación. Se puede encontrar un ejemplo en SimpleTimeLimiter.throwCause().

Una cosa que se destaca mirando este comportamiento es problemas casi todos ellos están abordando que no debería estar resolviendo. Son útiles en el código de la biblioteca, p. en Iterables, pero si estoy implementando este comportamiento, probablemente debería preguntarme si no hay bibliotecas o utilidades que resuelvan esto por mí.

En todos los casos, yo diría que instanceof cheques solamente nunca deben usarse internamente como un detalle de implementación - es decir, la persona que llama de cualquier método que se basa en una comprobación instanceof no debería ser capaz de (fácilmente) dicen que es que hiciste. Por ejemplo, ImmutableList.copyOf() siempre devuelve ImmutableList. Como detalle de implementación, usa instanceof para evitar la construcción de nuevos ImmutableList s, pero eso no es necesario para proporcionar de manera aceptable el comportamiento esperado.

Como un lado, fue divertido encontrar tu nombre, Louis, mientras estaba cavando a través del código fuente de Guava. ¡Juro que no tenía ni idea!

+0

Buen análisis de una base de código grande y respetada (Guava). – kevinarpe

+0

Una biblioteca de utilidades no proporciona una buena muestra del uso del lenguaje Java. Por ejemplo, ¿cuántos patrones de diseño GoF usa? –

+0

Utilicé Guava como ejemplo de una * buena * base de código, no necesariamente representativa. Si hay otras razones * buenas * para usar 'instanceof' que me perdí, actualizaré la pregunta. En este momento, no estoy al tanto de ninguno. – dimo414

Cuestiones relacionadas