Modificadores de Acceso
Introducción
Uno de los principios básicos de los lenguajes orientados a objetos es la encapsulación, mediante la cual se garantiza que los datos de una clase solo son modificados por las operaciones apropiadas implementadas en los métodos de sus clases para preservar su invariante, las reglas que define la clase y el estado consistente de su estado.
El acceso a las propiedades y métodos se determina mediante las palabras reservadas de los modificadores de acceso, en Java hay cuatro modificadores de acceso que definen ámbitos de visibilidad de más restrictivos a menos restrictivos: Java proporciona cuatro tipos de acceso a los miembros de una clase:
- public
- private
- protected
- default
Por supuesto son palabras reservadas. A continuación, te explico a detalle cada uno de ellos.
Niveles de acceso
Public
Este modificador es el menos restrictivo, y no protege a los miembros de la clase. Una clase, propiedad, método, constructor o interfaz declaradas como public pueden ser accedidos desde cualquier otra clase. No existen restricciones en el acceso a los miembros de una clase. Incluso se pueden usar los miembros de una clase que está en otro paquete. Relativo a Herencia: Todos los métodos y variables públicas de una clase, son heredadas por sus subclases.
Para ejemplificar el uso de éste modificador, observa la siguiente clase, que define una variable y un método público, y que pertenece a un paquete diferente al que se encuentra el programa principal:
package otropaquete;
public class A {
public int unaVariable;
public void mostrarVariable(){
System.out.println("El valor de unaVariable es: " + this.unaVariable);
}
}
package proyectomodificadores;
import otropaquete.*;
public class ProyectoModificadores {
public static void main(String[] args) {
A objetoA = new A();
objetoA.unaVariable = 10;
objetoA.mostrarVariable();
}
}
Aspectos a tener en cuenta: Observa cómo la clase A se encuentra en el paquete llamado otropaquete. Para que main pueda utilizar la clase A, debe importar otropaquete, cómo puedes ver: import otropaquete.*; Debido a que unaVariable está declarada con acceso público, main puede modificar directamente su valor a través del operador punto. Por directamente me refiero a que no es necesario usar un setter para asignarle un valor. El método mostrarVariable también es público, por lo que main también lo puede invocar sin problema.
Private
Los miembros y los métodos de una clase declarados de esta forma, permanecen privados para las clases externas, y sólo son accesibles dentro de la clase en la que se están declarando.
- Todos los miembros declarados como private sólo pueden ser accedidos dentro de la clase, pero no fuera de ella.
- Las variables que son declaradas private pueden ser accedidas fuera de la clase sólo si los métodos setter y getters son públicos. Esta regla se cumple aún con clases hijas (Herencia).
- El modificador de acceso privado es el nivel de acceso más restringido, por lo que ni las clases, ni los constructores pueden ser private (en la mayoría de las situaciones).
En general, se aconseja utilizar private para declarar variables de una clase, y a los métodos de acceso getters y setters tipo public.
Protected
Lo veremos con Herencia, esto es un adelanto. Este modificador de acceso busca proteger a los miembros declarados de esta forma. No es el nivel de protección más alto que tiene Java. En este nivel de acceso:
- Las variables, métodos y constructores que son declarados protected en una superclase pueden ser accedidos solo por las subclases en otro paquete o cualquier clase dentro del paquete de la clase con los miembros protegidos.
- El acceso protegido les da a las clases hijas la oportunidad de usar el método o variable mientras que a su vez lo protege de que clases no relacionadas traten de acceder a ellos.
- Los miembros protected de una superclase sólo están accesibles para la superclase, la subclase y otras clases del MISMO PAQUETE.
- Este modificador de acceso no puede ser aplicado a clases e interfaces, sólo a miembros y métodos. Observa el siguiente ejemplo. La clase A es la clase padre de B, y ambas se encuentran en el paquete otropaquete.
package otropaquete;
public class A {
public int unaVariable;
protected int otraVariable;
public void mostrarVariable(){
System.out.println("El valor de unaVariable es: " + this.unaVariable);
}
}
El atributo de A llamado otraVariable ha sido declarado como protected. Por esa razón, B puede utilizarlo directamente y modificar su valor como si estuviera declarado en B. Eso se realiza en un método que también tiene acceso protegido llamado modificarOtraVariable.
package otropaquete;
public class B extends A{
protected void modificarOtraVariable(){
this.otraVariable = 18;
}
}
La clase principal ProyectoModificadores está en un paquete diferente, llamado proyectomodificadores. En el método main se declaran dos objetos, uno de la clase A y otro de la clase B. El atributo unavariable tiene acceso público, eso ya lo habíamos discutido arriba. Pero el método modificarOtraVariable tiene acceso protegido, y como B está en otro paquete, no puede ser accedido, de ahí que se presente el error que debería salirte:
package proyectomodificadores;
import otropaquete.*;
public class ProyectoModificadores {
public static void main(String[] args) {
A objetoA = new A();
B objetoB = new B();
objetoA.unaVariable = 10;
objetoA.mostrarVariable();
objetoB.modificarOtraVariable();
}
}
Default
Cuando no utilizas un modificador de acceso específico (public, private, o protected), Java le asigna uno por default a la variable o método que estés declarando. Este se conoce como el modificador de acceso por defecto o por default. Este nivel de acceso:
- Permite a la clase y a TODAS las clases del mismo paquete acceder a los miembros y/o métodos (semejante a public)
- Pero las clases que no estén en el mismo paquete no podrán acceder a los miembros y/o métodos.
Es un error común olvidar escribir el modificador de acceso al declarar una variable o definir un método. Pero de esa manera sin querer le estarás dando acceso a todas las clases del paquete y eso no necesariamente es bueno.
Encapsulamiento
Muchos gente interpretan que los modo de acceso se aplica a “nivel de objeto”, pero OJO, se aplican a “nivel de clase”. Quiere decir esto que si obj1 y obj2 son objetos de la misma clase, obj2 tiene acceso a los miembros private de obj1 y viceversa. Esta observación es de la máxima importancia cuando tenemos clases con métodos que referencia a objetos de su misma clase, por ejemplo:
class MiClase {
private int conAccesoPrivado;
public int conAccesoPublico;
int conAccesoPaquete; //en este caso es equivalente a public
void setConAccesoPrivado(int i){
conAccesoPrivado=i;
}
int getConAccesoPrivado(){
return conAccesoPrivado;
}
void imprimeOtroObjeto(MiClase o){
System.out.println("Desde obj2 puedo acceder a los miembros publicos y privados de obj1");
System.out.println("el atributo privado vale: " + o.conAccesoPrivado); //no da error
System.out.println("el atributo publico: " + o.conAccesoPublico);
}
}
class Unidad4{
public static void main(String[] args) {
MiClase obj1 = new MiClase();
obj1.setConAccesoPrivado(5);
obj1.conAccesoPublico=6;
//demuestro que desde otro objeto obj2 de la misma clase que obj1 puedo acceder a lo privado de obj1
MiClase obj2 = new MiClase();
obj2.imprimeOtroObjeto(obj1);
}
}
Los niveles de acceso te pueden ayudar al respecto.
- usa el nivel más restrictivo que tenga sentido para un miembro. Usa private a no ser que tengas una buena razón para no hacerlo
- evita campos públicos excepto para constantes ( muchos ejemplos en este tutorial usan campos públicos. Esto nos puede ayudar a ilustrar algunos puntos con concisión, pero no se recomienda para código real en explotación). Los campos públicos tienden a encadenarte a una implementación concreta y a limitar tu flexibilidad al respecto de cambios en tu código.
Extra: Modularidad
Los ámbitos de visibilidad es un mecanismo bastante limitado ni es suficiente para proporcionar encapsulación. No hay ningún impedimento a que cualquiera pueda crear una clase en un paquete que contiene clases privadas de paquete o métodos package private o heredar de esas clases y de esta manera tener acceso a clases, métodos y propiedades que el autor original no las diseñó para esos propósitos. Puede ser incluso un problema de seguridad.
La modularidad añadida en Java 9 viene a complementar y dar una solución más completa a los ámbitos de visibilidad así como garantizar mejor la encapsulación tal y como el programador del paquete original ha diseñado. Por si os interesa indagar: enlace
Ejercicio 1
Pon los atributos private y haz los cambios necesarios:
class Racional{
int numerador;
int denominador;
Racional(int numerador, int denominador){
this.numerador=numerador;
this.denominador=denominador;
}
static Racional multiplicar(Racional r1, Racional r2){
Racional resultado= new Racional(1,1);
resultado.numerador=r1.numerador*r2.numerador;
resultado.denominador=r1.denominador*r2.denominador;
return resultado;
}
}
class Unidad4{
public static void main(String[] args) {
Racional r1=new Racional(3,4);
Racional r2=new Racional(1,2);
Racional r3=new Racional(1,1);
r3=Racional.multiplicar(r1, r2);
System.out.println("MUTIPLICACIÓN DE NÚMEROS RACIONALES");
System.out.println("r1 vale: "+r1.numerador+"/"+r1.denominador);
System.out.println("r2 vale: "+r2.numerador+"/"+r2.denominador);
System.out.println("r3 vale: "+r3.numerador+"/"+r3.denominador);
}
}
Ejercicio 2
Pregunta 1: Diga si sale error de compilación esto
package entidades;
private class Cliente {
}
package formato;
import entidades.Cliente;
public class FormatoExcel {
Cliente c = new Cliente();
}
Pregunta 2: Diga si sale error de compilación
package entidades;
class Cliente {
}
package formato;
import entidades.Cliente;
public class FormatoExcel {
}
Pregunta 3: Diga si sale error de compilación
package entidades;
class Cliente {
}
package entidades;
public class FormatoExcel {
Cliente c = new Cliente();
}
Pregunta 4: Diga si sale error de compilación
package entidades;
public class Cliente {
int demo;
}
package entidades;
public class FormatoExcel {
Cliente c = new Cliente();
public void método(){
c.demo = 0;
}
}
Pregunta 5: Diga si sale error de compilación
package entidades;
class Cliente {
public int demo;
}
package formato;
import entidades.Cliente;
public class FormatoExcel {
Cliente c = new Cliente();
public void método(){
c.demo = 0;
}
}