Me gustaría poder escribir una clase Java en un paquete que pueda acceder a métodos no públicos de una clase en otro paquete sin tener que convertirla en una subclase de la otra clase. es posible?
Aquí hay un pequeño truco que uso en JAVA para replicar el mecanismo amigo de C ++.
Digamos que tengo una clase Romeo
y otra clase Juliet
. Están en diferentes paquetes (familia) por razones de odio.
Romeo
quiere cuddle
Juliet
y Juliet
solo quiere dejarla Romeo
cuddle
.
En C ++, Juliet
declararía Romeo
como un (amante) friend
pero no hay tales cosas en Java.
Aquí están las clases y el truco:
Mujeres primero :
package capulet;
import montague.Romeo;
public class Juliet {
public static void cuddle(Romeo.Love love) {
Objects.requireNonNull(love);
System.out.println("O Romeo, Romeo, wherefore art thou Romeo?");
}
}
Entonces el método Juliet.cuddle
es public
pero necesitas un Romeo.Love
para llamarlo. Utiliza esto Romeo.Love
como una "seguridad de firma" para garantizar que solo se Romeo
pueda llamar a este método y comprueba que el amor es real para que el tiempo de ejecución arroje un NullPointerException
si es así null
.
Ahora muchachos:
package montague;
import capulet.Juliet;
public class Romeo {
public static final class Love { private Love() {} }
private static final Love love = new Love();
public static void cuddleJuliet() {
Juliet.cuddle(love);
}
}
La clase Romeo.Love
es pública, pero su constructor sí private
. Por lo tanto, cualquiera puede verlo, pero solo Romeo
puede construirlo. Utilizo una referencia estática para Romeo.Love
que la que nunca se usa solo se construya una vez y no afecte a la optimización.
Por lo tanto, Romeo
puede cuddle
Juliet
y sólo él puede, porque sólo él puede construir y acceder a un Romeo.Love
ejemplo, que es requerido por Juliet
a cuddle
ella (o de lo que ella te da palmadas con una NullPointerException
).
Romeo
'sLove
para elJulia
eterno cambiando ellove
campo para serfinal
;-).Los diseñadores de Java rechazaron explícitamente la idea de amigo, ya que funciona en C ++. Pones a tus "amigos" en el mismo paquete. La seguridad privada, protegida y empaquetada se aplica como parte del diseño del lenguaje.
James Gosling quería que Java fuera C ++ sin los errores. Creo que sintió que ese amigo fue un error porque viola los principios de la OOP. Los paquetes proporcionan una forma razonable de organizar los componentes sin ser demasiado purista acerca de la POO.
NR señaló que puedes hacer trampa usando la reflexión, pero incluso eso solo funciona si no estás usando SecurityManager. Si activa la seguridad estándar de Java, no podrá hacer trampa con reflexión a menos que escriba una política de seguridad para permitirla específicamente.
fuente
friend
violaba la POO (en particular, más que el acceso al paquete), entonces realmente no lo entendió (completamente posible, muchas personas lo malinterpretan).El concepto 'amigo' es útil en Java, por ejemplo, para separar una API de su implementación. Es común que las clases de implementación necesiten acceso a los componentes internos de la clase API, pero estos no deben exponerse a los clientes API. Esto se puede lograr usando el patrón 'Friend Accessor' como se detalla a continuación:
La clase expuesta a través de la API:
La clase que proporciona la funcionalidad 'amigo':
Ejemplo de acceso desde una clase en el paquete de implementación 'amigo':
fuente
Hay dos soluciones a su pregunta que no implican mantener todas las clases en el mismo paquete.
El primero es usar el patrón de Accesor de amigos / Paquete de amigos descrito en (Diseño práctico de API, Tulach 2008).
El segundo es usar OSGi. Hay un artículo aquí explicando cómo OSGi logra esto.
Preguntas relacionadas: 1 , 2 y 3 .
fuente
Que yo sepa, no es posible.
Tal vez, podría darnos más detalles sobre su diseño. Preguntas como estas son probablemente el resultado de fallas de diseño.
Solo considera
fuente
La respuesta de eirikma es fácil y excelente. Podría agregar una cosa más: en lugar de tener un método de acceso público, getFriend () para obtener un amigo que no se puede usar, podría ir un paso más allá y no permitir obtener al amigo sin un token: getFriend (Service.FriendToken). Este FriendToken sería una clase pública interna con un constructor privado, por lo que solo el Servicio podría crear una instancia.
fuente
Aquí hay un claro ejemplo de caso de uso con una
Friend
clase reutilizable . El beneficio de este mecanismo es la simplicidad de uso. Quizás sea bueno para dar a las clases de prueba unitarias más acceso que el resto de la aplicación.Para comenzar, aquí hay un ejemplo de cómo usar la
Friend
clase.Luego, en otro paquete, puede hacer esto:
los
Friend
clase es la siguiente.Sin embargo, el problema es que se puede abusar así:
Ahora, puede ser cierto que la
Other
clase no tiene constructores públicos, por lo que elAbuser
código anterior es imposible. Sin embargo, si su clase lo hace tener un constructor público, entonces es probable que sea aconsejable duplicar la clase de amigo como una clase interna. Tome estaOther2
clase como ejemplo:Y luego la
Owner2
clase sería así:Tenga en cuenta que la
Other2.Friend
clase tiene un constructor privado, por lo que esta es una forma mucho más segura de hacerlo.fuente
La solución provista quizás no fue la más simple. Otro enfoque se basa en la misma idea que en C ++: los miembros privados no son accesibles fuera del paquete / ámbito privado, a excepción de una clase específica que el propietario hace un amigo de sí mismo.
La clase que necesita acceso amigo a un miembro debe crear una "clase amiga" abstracta pública interna a la que la clase propietaria de las propiedades ocultas puede exportar el acceso, devolviendo una subclase que implementa los métodos de implementación de acceso. El método "API" de la clase amigo puede ser privado, por lo que no es accesible fuera de la clase que necesita acceso amigo. Su única declaración es una llamada a un miembro protegido abstracto que implementa la clase exportadora.
Aquí está el código:
Primero, la prueba que verifica que esto realmente funciona:
Luego, el Servicio que necesita acceso amigo a un paquete privado miembro de la Entidad:
Finalmente: la clase Entity que proporciona acceso amigable a un miembro privado del paquete solo a la clase application.service.Service.
De acuerdo, debo admitir que es un poco más largo que "servicio de amigos :: Servicio"; pero podría ser posible acortarlo mientras se conserva la verificación en tiempo de compilación mediante anotaciones.
fuente
En Java es posible tener una "amistad relacionada con el paquete". Esto puede ser útil para pruebas unitarias. Si no especifica privado / público / protegido frente a un método, será "amigo en el paquete". Una clase en el mismo paquete podrá acceder a ella, pero será privada fuera de la clase.
Esta regla no siempre se conoce, y es una buena aproximación de una palabra clave "amigo" de C ++. Me parece un buen reemplazo.
fuente
Creo que las clases de amigos en C ++ son como el concepto de clase interna en Java. Usando clases internas, puedes definir una clase de cierre y una cerrada. La clase adjunta tiene acceso completo a los miembros públicos y privados de su clase adjunta. ver el siguiente enlace: http://docs.oracle.com/javase/tutorial/java/javaOO/nested.html
fuente
Creo que el enfoque de usar el patrón de acceso amigo es demasiado complicado. Tuve que enfrentar el mismo problema y lo resolví usando el buen constructor de copias antiguo, conocido de C ++, en Java:
En su aplicación, podría escribir el siguiente código:
La ventaja de este método es que solo su aplicación tiene acceso a los datos protegidos. No es exactamente una sustitución de la palabra clave amigo. Pero creo que es bastante adecuado cuando escribe bibliotecas personalizadas y necesita acceder a datos protegidos.
Siempre que tenga que lidiar con instancias de ProtectedContainer, puede envolver su ProtectedAccessor a su alrededor y obtener acceso.
También funciona con métodos protegidos. Los define protegidos en su API. Más adelante en su aplicación, escribe una clase de contenedor privado y expone el método protegido como público. Eso es.
fuente
ProtectedContainer
se puede subclasificar fuera del paquete!Si desea acceder a métodos protegidos, puede crear una subclase de la clase que desee usar que exponga los métodos que desea usar como públicos (o internos para que el espacio de nombres sea más seguro) y tener una instancia de esa clase en su clase (Úselo como un proxy).
En lo que respecta a los métodos privados (creo), no tienes suerte.
fuente
Estoy de acuerdo en que en la mayoría de los casos la palabra clave amigo es innecesaria.
Y finalmente, si realmente es necesario, existe el patrón de acceso amigo mencionado en las otras respuestas.
fuente
No usar una palabra clave más o menos.
Podrías "hacer trampa" usando la reflexión, etc., pero no recomendaría "hacer trampa".
fuente
Un método que he encontrado para resolver este problema es crear un objeto de acceso, así:
El primer código para llamar
getAccessor()
"reclamaciones de propiedad" del descriptor de acceso. Por lo general, este es el código que crea el objeto.Esto también tiene una ventaja sobre el mecanismo amigo de C ++, ya que le permite limitar el acceso en un nivel por instancia , en lugar de un nivel por clase . Al controlar la referencia del descriptor de acceso, usted controla el acceso al objeto. También puede crear múltiples accesores y dar acceso diferente a cada uno, lo que permite un control detallado sobre qué código puede acceder a qué:
Finalmente, si desea que las cosas estén un poco más organizadas, puede crear un objeto de referencia, que mantenga todo junto. Esto le permite reclamar todos los accesos con una llamada de método, así como mantenerlos junto con su instancia vinculada. Una vez que tenga la referencia, puede pasar los accesos al código que lo necesita:
Después de muchos golpes en la cabeza (no del tipo bueno), esta fue mi solución final, y me gusta mucho. Es flexible, fácil de usar y permite un muy buen control sobre el acceso a clases. (El acceso solo con referencia es muy útil). Si usa protegido en lugar de privado para los accesores / referencias, las subclases de Foo pueden incluso devolver referencias extendidas
getReference
. Tampoco requiere ningún reflejo, por lo que se puede usar en cualquier entorno.fuente
A partir de Java 9, los módulos se pueden utilizar para que esto no sea un problema en muchos casos.
fuente
Prefiero delegación o composición o clase de fábrica (dependiendo del tema que resulte en este problema) para evitar que sea una clase pública.
Si se trata de un problema de "clases de interfaz / implementación en diferentes paquetes", utilizaría una clase de fábrica pública que estaría en el mismo paquete que el paquete impl y evitaría la exposición de la clase impl.
Si es un problema de "Odio hacer pública esta clase / método solo para proporcionar esta funcionalidad para otra clase en un paquete diferente", entonces usaría una clase de delegado público en el mismo paquete y expondría solo esa parte de la funcionalidad necesitado por la clase "outsider".
Algunas de estas decisiones son impulsadas por la arquitectura de carga de clases del servidor de destino (paquete OSGi, WAR / EAR, etc.), las convenciones de implementación y nomenclatura de paquetes. Por ejemplo, la solución propuesta anteriormente, el patrón 'Friend Accessor' es inteligente para las aplicaciones normales de Java. Me pregunto si se vuelve difícil implementarlo en OSGi debido a la diferencia en el estilo de carga de clases.
fuente
No sé si es de alguna utilidad para alguien, pero lo manejé de la siguiente manera:
Creé una interfaz (AdminRights).
Cada clase que debería poder llamar a dichas funciones debería implementar AdminRights.
Luego creé una función HasAdminRights de la siguiente manera:
fuente
Una vez vi una solución basada en la reflexión que hacía "comprobación de amigos" en tiempo de ejecución utilizando la reflexión y comprobando la pila de llamadas para ver si la clase que llamaba al método tenía permitido hacerlo. Al ser un control de tiempo de ejecución, tiene el inconveniente obvio.
fuente