¿Cuál es la diferencia entre público, protegido, paquete privado y privado en Java?
En Java, ¿existen reglas claras sobre cuándo usar cada uno de los modificadores de acceso, es decir, el predeterminado (paquete privado), public
y protected
, private
al crear class
y interface
tratar con la herencia?
El tutorial oficial puede resultarle útil.
Clase | Paquete | Subclase (mismo paquete) |
Subclase (paquete de diferencias) |
Mundo | |
---|---|---|---|---|---|
public |
+ | + | + | + | + |
protected |
+ | + | + | + | |
sin modificador | + | + | + | ||
private |
+ |
+ : accesible
en blanco : no accesible
(Advertencia: no soy un programador de Java, soy un programador de Perl. Perl no tiene protecciones formales y quizás por eso entiendo tan bien el problema :))
Privado
Como se podría pensar, sólo la clase en la que está declarado puede verlo.
Paquete Privado
Sólo puede ser visto y utilizado por el paquete en el que fue declarado. Este es el valor predeterminado en Java (que algunos ven como un error).
Protegido
El paquete Private + puede ser visto por subclases o miembros del paquete.
Público
Todos pueden verlo.
Publicado
Visible fuera del código que controlo. (Aunque no es la sintaxis de Java, es importante para esta discusión).
C++ define un nivel adicional llamado "amigo" y cuanto menos sepas sobre eso, mejor.
¿Cuándo deberías usar qué? La idea es encapsular para ocultar información. En la medida de lo posible, desea ocultar a sus usuarios los detalles de cómo se hace algo. ¿Por qué? Porque entonces podrás cambiarlos más tarde y no romper el código de nadie. Esto le permite optimizar, refactorizar, rediseñar y corregir errores sin preocuparse de que alguien estuviera usando el código que acaba de revisar.
Por lo tanto, la regla general es hacer que las cosas sean tan visibles como deben ser. Comience con privado y agregue solo más visibilidad según sea necesario. Sólo haga público lo que sea necesario para que el usuario sepa; cada detalle que haga público limita su capacidad de rediseñar el sistema.
Si desea que los usuarios puedan personalizar comportamientos, en lugar de hacer públicos los elementos internos para poder anularlos, a menudo es una mejor idea meter esas agallas en un objeto y hacer pública esa interfaz. De esa manera pueden simplemente conectar un nuevo objeto. Por ejemplo, si estuviera escribiendo un reproductor de CD y quisiera personalizar el bit "ir a buscar información sobre este CD", en lugar de hacer públicos esos métodos, pondría toda esa funcionalidad en su objeto y haría público solo su captador/definidor de objetos. De esta manera, ser tacaño a la hora de exponer las entrañas fomenta una buena composición y separación de preocupaciones.
Me quedo sólo con "privado" y "público". Muchos lenguajes OO simplemente tienen eso. "Protegido" puede ser útil, pero es una trampa. Una vez que una interfaz es más que privada, queda fuera de tu control y tienes que buscar en el código de otras personas para encontrar usos.
Aquí es donde entra en juego la idea de "publicado". Cambiar una interfaz (refactorizarla) requiere que encuentres todo el código que la utiliza y lo cambies también. Si la interfaz es privada, no hay problema. Si está protegido, tienes que buscar todas tus subclases. Si es público, debes buscar todo el código que usa tu código. A veces esto es posible, por ejemplo, si está trabajando en código corporativo que es solo para uso interno, no importa si una interfaz es pública. Puede obtener todo el código del repositorio corporativo. Pero si se "publica" una interfaz, si hay un código que la utiliza fuera de su control, entonces está arruinado. Debe admitir esa interfaz o correr el riesgo de romper el código. Incluso las interfaces protegidas pueden considerarse publicadas (por eso no me molesto en protegerlas).
Muchas lenguas consideran que la naturaleza jerárquica de público/protegido/privado es demasiado limitante y no se ajusta a la realidad. Para ese fin, existe el concepto de clase de rasgo , pero eso es otro espectáculo.
Aquí hay una versión mejor de la tabla, que también incluye una columna para módulos.
Explicaciones
Solo se puede acceder a un miembro privado
i
( ) dentro de la misma clase en la que está declarado.Solo se puede acceder a un miembro sin modificador de acceso (
j
) dentro de las clases del mismo paquete.Se puede acceder a un miembro protegido
k
( ) dentro de todas las clases del mismo paquete y dentro de las subclases de otros paquetes.Un miembro público
l
( ) es accesible para todas las clases (a menos que resida en un módulo que no exporte el paquete en el que está declarado).
¿Qué modificador elegir?
Los modificadores de acceso son una herramienta que le ayudará a evitar romper accidentalmente la encapsulación (*) . Pregúntese si pretende que el miembro sea algo interno a la clase, paquete, jerarquía de clases o no interno en absoluto, y elija el nivel de acceso en consecuencia.
Ejemplos:
- Un campo
long internalCounter
probablemente debería ser privado ya que es mutable y un detalle de implementación. - Una clase de la que sólo se debe crear una instancia en una clase de fábrica (en el mismo paquete) debe tener un constructor restringido por paquete, ya que no debería ser posible llamarlo directamente desde fuera del paquete.
- Se debe proteger un método interno
void beforeRender()
llamado justo antes de renderizar y utilizado como enlace en subclases. - Un
void saveGame(File dst)
método que se llama desde el código GUI debe ser público.
(*) ¿Qué es exactamente la Encapsulación?
____________________________________________________________________
| highest precedence <---------> lowest precedence
*———————————————+———————————————+———————————+———————————————+———————
\ xCanBeSeenBy | this | any class | this subclass | any
\__________ | class | in same | in another | class
\ | nonsubbed | package | package |
Modifier of x \ | | | |
————————————————*———————————————+———————————+———————————————+———————
public | ✔ | ✔ | ✔ | ✔
————————————————+———————————————+———————————+———————————————+———————
protected | ✔ | ✔ | ✔ | ✘
————————————————+———————————————+———————————+———————————————+———————
package-private | | | |
(no modifier) | ✔ | ✔ | ✘ | ✘
————————————————+———————————————+———————————+———————————————+———————
private | ✔ | ✘ | ✘ | ✘
____________________________________________________________________
Regla fácil. Comience declarando todo privado. Y luego avanzar hacia el público a medida que surjan las necesidades y el diseño lo amerite.
Al exponer a los miembros, pregúntese si está exponiendo opciones de representación o de abstracción. El primero es algo que desea evitar, ya que introducirá demasiadas dependencias en la representación real en lugar de su comportamiento observable.
Como regla general, trato de evitar anular las implementaciones de métodos mediante subclases; Es demasiado fácil arruinar la lógica. Declare métodos abstractos protegidos si desea anularlos.
Además, utilice la anotación @Override al anular para evitar que las cosas se rompan al refactorizar.