Tengo un problema con la ocultación de nombres que es extremadamente difícil de resolver. Aquí hay una versión simplificada que explica el problema:
Hay una clase: org.A
package org;
public class A{
public class X{...}
...
protected int net;
}
Entonces hay una clase net.foo.X
package net.foo;
public class X{
public static void doSomething();
}
Y ahora, aquí está la clase problemática que hereda A
y quiere llamarnet.foo.X.doSomething()
package com.bar;
class B extends A {
public void doSomething(){
net.foo.X.doSomething(); // doesn't work; package net is hidden by inherited field
X.doSomething(); // doesn't work; type net.foo.X is hidden by inherited X
}
}
Como ve, esto no es posible. No puedo usar el nombre simple X
porque está oculto por un tipo heredado. No puedo usar el nombre completo net.foo.X
porque net
está oculto por un campo heredado.
Solo la clase B
está en mi base de código; las clases net.foo.X
y org.A
son clases de biblioteca, ¡así que no puedo alterarlas!
Mi única solución se ve así: podría llamar a otra clase que a su vez llama X.doSomething()
; pero esta clase solo existiría debido al choque de nombres, ¡que parece muy complicado! ¿No hay solución en la que puedo llamar directamente X.doSomething()
desde B.doSomething()
?
En un lenguaje que permite especificar el espacio de nombres global, por ejemplo, global::
en C # o ::
en C ++, podría simplemente prefijar net
este prefijo global, pero Java no lo permite.
fuente
public void help(net.foo.X x) { x.doSomething(); }
y llamar conhelp(null);
net.foo.X
tiene el método, noorg.A.X
!A
? La herencia puede ser tan desagradable, como has descubierto ...I could call another class that in turn calls X.doSomething(); but this class would only exist because of the name clash, which seems very messy
+1 por actitud de código limpio. Pero para mí, parece que esta es una situación en la que debería hacer una compensación. Simplemente haga esto y envíe un comentario largo y agradable sobre por qué tuvo que hacerlo (probablemente con un enlace a esta pregunta).Respuestas:
Puede convertir un
null
al tipo y luego invocar el método en eso (lo que funcionará, ya que el objeto de destino no está involucrado en la invocación de métodos estáticos).Esto tiene los beneficios de
net.foo.X
),B
el nombre que desea que tenga; es por esoimport static
que a no funcionará en su caso exacto),¡La desventaja es que este código es realmente horrible! Para mí, genera una advertencia y eso es algo bueno en general. Pero dado que está solucionando un problema que de otro modo no sería práctico, agregar un
en un punto de inclusión apropiado (¡mínimo!) cerrará el compilador.
fuente
doSomething
método en alguna parte de tu jerarquía), así que sí, la mejor solución.Probablemente la forma más simple (no necesariamente la más fácil) de administrar esto sería con una clase delegada:
y entonces ...
Esto es algo detallado, pero muy flexible: puede hacer que se comporte de la forma que desee; además, funciona de la misma manera tanto con
static
métodos como con objetos instanciadosMás sobre objetos delegados aquí: http://en.wikipedia.org/wiki/Delegation_pattern
fuente
Puede utilizar una importación estática:
Tenga cuidado con eso
B
yA
no contenga métodos con nombredoSomething
fuente
doSomething
como el nombre del método enB
…doSomething
algún lugar en la jerarquía de herencia (debido a cómo se resuelven los destinos de llamadas a métodos). Entonces Knuth te ayudará si quieres llamar a untoString
método. La solución propuesta por Donal es la que está en el libro Java Puzzlers de Bloch (creo que no lo he buscado), por lo que podemos considerarla la respuesta autorizada realmente :-)La forma correcta de hacer las cosas sería la importación estática, pero en el peor de los casos, PODRÍA construir una instancia de la clase usando la reflexión si conoce su nombre completo.
Java: newInstance de clase que no tiene un constructor predeterminado
Y luego invoca el método en la instancia.
O simplemente invoque el método en sí con reflexión: invocando un método estático usando reflexión
Por supuesto, estos son obviamente los últimos recursos.
fuente
static import
función se agregó solo enJava 1.5
. No envidio a las personas que necesitan desarrollar para 1.4 o menos, tuve que hacerlo una vez y fue terrible.((net.foo.X)null).doSomething()
solución que funciona en el antiguo java. Pero si el tipoA
incluso contiene un tipo internonet
, ENTONCES esta es la única respuesta que sigue siendo válida :).No es realmente LA respuesta, pero puede crear una instancia de X y llamar al método estático en ella. Esa sería una forma (sucia, lo admito) de llamar a tu método.
fuente
null
al tipo y luego llamar al método sería mejor, ya que crear un objeto puede tener efectos secundarios que no deseo.null
parece ser una de las formas más limpias de hacerlo. Necesita@SuppressWarnings("static-access")
estar libre de advertencias ...null
, pero el castingnull
siempre me ha roto el corazón.No es necesario realizar ningún lanzamiento o suprimir advertencias extrañas o crear una instancia redundante. Solo un truco usando el hecho de que puede llamar a métodos estáticos de la clase principal a través de la subclase. (Similar a mi solución pirateada aquí ).
Solo crea una clase como esta
(El constructor privado en esta clase final se asegura de que nadie pueda crear accidentalmente una instancia de esta clase).
Entonces puedes llamar a
X.doSomething()
través de él:fuente
¿Qué sucede si intenta obtener el espacio de nombres global dado que todos los archivos están en la misma carpeta? ( http://www.beanshell.org/javadoc/bsh/class-use/NameSpace.html )
fuente
getGlobal()
no es un método estándar de Java para paquetes ... (creo que los paquetes no pueden tener ningún método en Java ...)Ésta es una de las razones por las que la composición es preferible a la herencia.
fuente
Usaría el patrón de estrategia.
Ahora también ha desacoplado su dependencia, por lo que sus pruebas unitarias serán más fáciles de escribir.
fuente
implements SomethingStrategy
en laXSomethingStrategy
declaración de clase.