Ya revisamos como funcionan los modificadores de acceso a nivel de clase. Ahora veamos los modificadores de acceso a nivel de métodos y propiedades de clases. En este caso el número de modificadores aumenta, ahora tenemos cuatro de ellos:
- public
- private
- default
- protected
Revisemos cada uno de ellos. El modificador de acceso public nos permite que nuestra propiedad o método sea accesible desde cualquier parte de nuestro programa, no hay restricciones (excepto si se usa la funcionalidad de módulos añadida a partir de Java 9, pero ese es tema para otro post). Veamos el siguiente ejemplo:
// Archivo MiClase1.java
package com.package1;
public class MiClase1{
public String miVar = "Valor inicial";
public void saludar(){
System.out.println("Hola");
}
}
Es una clase sencilla llamada MiClase1, sin nada fuera de lo común en donde tiene una variable y un método, ambos públicos. El siguiente ejemplo es otro fragmento de código de una clase llamada MiClase2 desde el cual se crea un objeto de tipo MiClase1 y usa sus atributos/propiedades:
// Archivo MiClase2.java
package com.package2;
import com.package1.MiClase1;
public class MiClase2{
public static void main(String[] args){
MiClase1 obj = new MiClase1();
System.out.println(obj.miVar);
obj.miVar = "Valor nuevo";
System.out.println(obj.miVar);
obj.saludar();
}
}
Nótese que MiClase2 pertenece a un paquete diferente del que pertenece MiClase1, y a pesar de ello, MiClase2 tuvo acceso a los elementos de MiClase1. El resultado de ejecutar MiClase2 (tiene un public static void main, asi que podemos ejecutar) es el siguiente:
Valor inicial
Valor nuevo
Hola
Cabe notar que incluso fue posible modificar el valor de miVar debido a que es public. Esto podría ser muy peligroso dependiendo de la funcionalidad que tenga la clase en cuestión. Que tal si se empieza a modificar indiscriminadamente el valor de esa variable por los diferentes programadores del equipo, llevando a comportamientos inesperados en nuestro programa. Es necesario restringir algunas veces (yo diría casi siempre) las variables de una clase para así evitar problemas. Esta cuestión de ocultación del estado o de los datos miembro de un objeto se llama encapsulamiento, pero eso es tema para otro post. Ahora bien, ¿Cómo evitamos que otros devs esten modificando la variable miVar de mi clase MiClase1? Les presento al modificador de acceso private. Modifiquemos un poco MiClase1 de la siguiente manera:
// Archivo MiClase1.java
package com.package1;
public class MiClase1{
private String miVar = "Valor inicial";
public void saludar(){
System.out.println("Hola");
}
}
Ahora miVar tiene el modificador de acceso private. Esto provocará que dicha variable solo sea accesible desde dentro de MiClase1, provocando que MiClase2 tenga un error y no se pueda compilar:
// Archivo MiClase2.java
package com.package2;
import com.package1.MiClase1;
public class MiClase2{
public static void main(String[] args){
MiClase1 obj = new MiClase1();
System.out.println(obj.miVar); // Error aqui
obj.miVar = "Valor nuevo"; // Error aqui
System.out.println(obj.miVar); // Error aqui
obj.saludar();
}
}
Lo mismo aplicaría si ponemos private el método saludar de MiClase1, ya no se podría invocar saludar desde fuera de su clase.
Ahora veamos el modificador de acceso default. A continuación se muestra nuevamente MiClase1 con algunos ligeros cambios:
// Archivo MiClase1.java
package com.package1;
public class MiClase1{
String miVar = "Valor inicial";
void saludar(){
System.out.println("Hola");
}
}
Ahora tanto la variable miVar como el método saludar no tienen modificador de acceso….. bueno, en realidad si tienen uno. Cuando no les escribimos un modificador de acceso, en realidad reciben el modificador de acceso default. ¿Y que rayos hace default? Pues lo que hace es que el elemento sea accesible solo desde adentro del mismo paquete. Esto seguiría provocando un error en MiClase2 porque MiClase2 pertenece a un paquete diferente de MiClase1:
// Archivo MiClase2.java
package com.package2;
import com.package1.MiClase1;
public class MiClase2{
public static void main(String[] args){
MiClase1 obj = new MiClase1();
System.out.println(obj.miVar); // Error aqui
obj.miVar = "Valor nuevo"; // Error aqui
System.out.println(obj.miVar); // Error aqui
obj.saludar(); // Error aqui
}
}
Una vez que cambiamos a MiClase2 para que pertenezca al mismo paquete de MiClase1 (osea a com.package1) los problemas de compilación desaparecen.
// Archivo MiClase2.java
package com.package1;
import com.package1.MiClase1; // Esta linea ahora la podemos omitir
public class MiClase2{
public static void main(String[] args){
MiClase1 obj = new MiClase1();
System.out.println(obj.miVar); // Ya no hay error aquí
obj.miVar = "Valor nuevo"; // Ya no hay error aquí
System.out.println(obj.miVar); // Ya no hay error aquí
obj.saludar(); // Ya no hay error aquí
}
}
Cabe notar que como ahora ambas clases pertenecen al mismo paquete, la linea 4, la que tiene el import, se puede quitar sin problemas porque al pertenecer MiClase1 al mismo paquete que MiClase2, no se requiere hacer un import entre ellas, podríamos dejar el import pero el compilador lanzará un simple warning que no afecta a la funcionalidad del programa pero es molesto.
Y finalmente, hablemos del modificador de acceso protected. Este modificador de acceso hace exactamente lo mismo que el modificador de acceso default, con la diferencia de que añade una cosita mas, la cual es que también permite el acceso a través de subclases en diferentes paquetes. Veamos un ejemplo, primero modifiquemos MiClase1 de la siguiente manera:
// Archivo MiClase1.java
package com.package1;
public class MiClase1{
protected String miVar = "Valor inicial";
protected void saludar(){
System.out.println("Hola");
}
}
Ahora miVar y saludar son protected. Ahora veamos a MiClase2 la cual usa elementos de MiClase1:
// Archivo MiClase2.java
package com.package1;
import com.package1.MiClase1; // Esta linea la podemos omitir
public class MiClase2{
public static void main(String[] args){
MiClase1 obj = new MiClase1();
System.out.println(obj.miVar);
obj.miVar = "Valor nuevo";
System.out.println(obj.miVar);
obj.saludar();
}
}
Todo va a funcionar bien, sin errores dentro de MiClase2 porque dicha clase pertenece al mismo paquete de MiClase1. Recuerda que el modificador protected hace lo mismo en este caso que default. Ahora bien ¿Que sucede si cambiamos de paquete a MiClase2 de la siguiente manera?
// Archivo MiClase2.java
package com.package2;
import com.package1.MiClase1;
public class MiClase2{
public static void main(String[] args){
MiClase1 obj = new MiClase1();
System.out.println(obj.miVar); // Error aqui
obj.miVar = "Valor nuevo"; // Error aqui
System.out.println(obj.miVar); // Error aqui
obj.saludar(); // Error aqui
}
}
Nuevamente volvieron los mismos errores puesto que miVar y saludar ya no son accesibles desde donde MiClase2 pertenece. Ahora bien ¿que pasaría si MiClase2 heredase de MiClase1? Veamos que sucede:
// Archivo MiClase2.java
package com.package2;
import com.package1.MiClase1;
public class MiClase2 extends MiClase1{
public static void main(String[] args){
MiClase2 obj = new MiClase2(); // Ahora creamos MiClase2 en vez de MiClase1
System.out.println(obj.miVar); // Todo Ok
obj.miVar = "Valor nuevo"; // Todo Ok
System.out.println(obj.miVar); // Todo Ok
obj.saludar(); // Todo Ok
}
}
Hemos creado un objeto de tipo MiClase2. A través de dicho objeto se puede acceder a miVar y saludar que pertenecen a MiClase1, osea que a través de la herencia pudimos acceder a las propiedades de MiClase1 que estaban con protected. Esto no hubiera sido posible si dichas propiedades tuvieran private o default. ¿Y que pasaría si desde otra clase cualquiera que pertenezca a otro paquete intentamos crear un objeto MiClase2 e usar las propiedades que vienen desde MiClase1? Veamos que sucede:
// Archivo OtraClaseCualquiera.java
package com.otropaquete;
import com.package2.MiClase2;
public class OtraClaseCualquiera {
public static void main(String[] args){
MiClase2 obj = new MiClase2();
System.out.println(obj.miVar); // Error aquí
obj.miVar = "Valor nuevo"; // Error aquí
System.out.println(obj.miVar); // Error aquí
obj.saludar(); // Error aquí
}
}
Como se puede observar la clase OtraClaseCualquiera que pertenece al paquete com.otropaquete tiene errores por lo mismo que platicamos anteriormente, incluso pasaría lo mismo si en ves de crear un objeto de tipo MiClase2 fuese de MiClase1. Podemos concluir que el modificador de acceso default es mas restrictivo que protected!!! Pregunta de examen 🙂