¿Hay alguna manera de simular el concepto de 'amigo' de C++ en Java?
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 paquetes diferentes (familiares) por motivos de odio.
Romeo
quiere cuddle
Juliet
y Juliet
sólo quiere dejarla Romeo
cuddle
.
En C++, Juliet
se declararía Romeo
como (amante) friend
pero no existen 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 "firma de seguridad" para garantizar que solo Romeo
se pueda llamar a este método y verifica que el amor sea real para que el tiempo de ejecución arroje un mensaje NullPointerException
si lo es null
.
Ahora chicos:
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 lo es private
. Por lo tanto cualquiera puede verlo, pero sólo Romeo
puede construirlo. Utilizo una referencia estática para que la Romeo.Love
que nunca se usa solo se construya una vez y no afecte la optimización.
Por lo tanto, Romeo
puede cuddle
Juliet
y solo él puede porque solo él puede construir y acceder a una Romeo.Love
instancia, que es requerida por Juliet
ella cuddle
(de lo contrario, ella te abofeteará con un NullPointerException
).
Los diseñadores de Java rechazaron explícitamente la idea de amigo tal como 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 errores. Creo que sintió que ese amigo era un error porque viola los principios de programación orientada a objetos. Los paquetes proporcionan una forma razonable de organizar componentes sin ser demasiado puristas con respecto a la programación orientada a objetos.
NR señaló que se podía 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 la reflexión a menos que escriba una política de seguridad que lo permita específicamente.
El concepto de "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 estar expuestos a los clientes API. Esto se puede lograr utilizando el patrón 'Friend Accessor' como se detalla a continuación:
La clase expuesta a través de la API:
package api;
public final class Exposed {
static {
// Declare classes in the implementation package as 'friends'
Accessor.setInstance(new AccessorImpl());
}
// Only accessible by 'friend' classes.
Exposed() {
}
// Only accessible by 'friend' classes.
void sayHello() {
System.out.println("Hello");
}
static final class AccessorImpl extends Accessor {
protected Exposed createExposed() {
return new Exposed();
}
protected void sayHello(Exposed exposed) {
exposed.sayHello();
}
}
}
La clase que proporciona la funcionalidad 'amigo':
package impl;
public abstract class Accessor {
private static Accessor instance;
static Accessor getInstance() {
Accessor a = instance;
if (a != null) {
return a;
}
return createInstance();
}
private static Accessor createInstance() {
try {
Class.forName(Exposed.class.getName(), true,
Exposed.class.getClassLoader());
} catch (ClassNotFoundException e) {
throw new IllegalStateException(e);
}
return instance;
}
public static void setInstance(Accessor accessor) {
if (instance != null) {
throw new IllegalStateException(
"Accessor instance already set");
}
instance = accessor;
}
protected abstract Exposed createExposed();
protected abstract void sayHello(Exposed exposed);
}
Ejemplo de acceso desde una clase en el paquete de implementación 'amigo':
package impl;
public final class FriendlyAccessExample {
public static void main(String[] args) {
Accessor accessor = Accessor.getInstance();
Exposed exposed = accessor.createExposed();
accessor.sayHello(exposed);
}
}