En Java, ¿por qué los miembros protegidos se hicieron accesibles a las clases del mismo paquete?


14

De la documentación oficial ...

Modificador Clase Paquete Subclase Mundo 
público YYYY 
protegido YYYN 
sin modificador YYNN 
privado YNNN 

La cuestión es que no recuerdo haber tenido un caso de uso en el que necesitaba acceder a miembros protegidos de una clase dentro del mismo paquete.

¿Cuáles fueron las razones detrás de esta implementación?

Editar: para aclarar, estoy buscando un caso de uso específico en el que tanto una subclase como una clase dentro del mismo paquete necesiten acceder a un campo o método protegido.

package some.package;
public class A {
 protected void protectedMethod(){
  // do something
 }
}

package another.package;
public class B extends A{
 public void someMethod(){
  // accessible because B is a subclass of A
  protectedMethod();
 }
} 

package some.package;
public class C {
 public void anotherMethod(){
  // accessible because C is in the same package as A
  protectedMehtod();
 }
}

Respuestas:


4

buscando un caso de uso específico en el que tanto una subclase como una clase dentro del mismo paquete necesiten acceder a un campo o método protegido ...

Bueno, para mí, este caso de uso es más general que específico, y se deriva de mis preferencias para:

  1. Comience con un modificador de acceso lo más estricto posible, recurriendo a uno o más débiles solo más tarde, según se considere necesario.
  2. Haga que las pruebas unitarias residan en el mismo paquete que el código probado.

Desde arriba, puedo comenzar a diseñar mis objetos con modificadores de acceso predeterminados (comenzaría con, privatepero eso complicaría las pruebas unitarias):

public class Example {
    public static void main(String [] args) {
        new UnitTest().testDoSomething(new Unit1(), new Unit2());
    }

    static class Unit1 {
        void doSomething() {} // default access
    }
    static class Unit2 {
        void doSomething() {} // default access
    }

    static class UnitTest {
        void testDoSomething(Unit1 unit1, Unit2 unit2) {
            unit1.doSomething();
            unit2.doSomething();
        }
    }
}

Nota al margen en el fragmento anterior Unit1, Unit2y UnitTestestán anidados dentro Examplepara simplificar la presentación, pero en un proyecto real, probablemente tendría estas clases en archivos separados (e UnitTestincluso en un directorio separado ).

Luego, cuando surja una necesidad, debilitaría el control de acceso de forma predeterminada a protected:

public class ExampleEvolved {
    public static void main(String [] args) {
        new UnitTest().testDoSomething(new Unit1(), new Unit2());
    }

    static class Unit1 {
        protected void doSomething() {} // made protected
    }
    static class Unit2 {
        protected void doSomething() {} // made protected
    }

    static class UnitTest {
        // ---> no changes needed although UnitTest doesn't subclass
        // ...and, hey, if I'd have to subclass... which one of Unit1, Unit2?
        void testDoSomething(Unit1 unit1, Unit2 unit2) {
            unit1.doSomething();
            unit2.doSomething();
        }
    }
}

Verá, puedo mantener el código de prueba de la unidad ExampleEvolvedsin cambios debido a que los métodos protegidos son accesibles desde el mismo paquete, a pesar de que acceder al objeto no es una subclase .

Se necesitan menos cambios => modificación más segura; después de todo, cambié solo los modificadores de acceso y no modifiqué qué métodos Unit1.doSomething()y Unit2.doSomething()qué hago, por lo que es natural esperar que el código de prueba de la unidad continúe ejecutándose sin modificaciones.


5

Yo diría que esto tiene dos partes:

  1. El acceso predeterminado "paquete" es útil en una amplia gama de casos, porque las clases no siempre son una buena unidad de encapsulación. Varios objetos compuestos donde algunos objetos actúan como una colección de otros objetos, pero los elementos no deben ser modificables públicamente, ya que hay algunos invariantes en toda la colección, por lo que la colección debe tener un acceso elevado a los elementos. C ++ tiene amigos, Java tiene acceso a paquetes.
  2. Ahora el alcance de acceso del "paquete" es básicamente independiente del alcance de la "subclase" (protegida). Por lo tanto, necesitaría especificadores de acceso adicionales solo para paquetes, solo subclases y paquetes y subclases. El alcance del "paquete" está más restringido ya que el conjunto de clases en un paquete generalmente es definitivo, mientras que una subclase puede aparecer en cualquier lugar. Entonces, para simplificar las cosas, Java simplemente incluye el acceso al paquete en el acceso protegido y no tiene un especificador adicional para las subclases, pero no el paquete. Aunque casi siempre deberías pensar protectedexactamente como eso.

¿No sería más simple si protectedes solo una subclase? Honestamente, durante mucho tiempo, tuve la impresión de que ese era el comportamiento
jramoyo

@jramoyo: No, porque aún necesitarías hacer que el comportamiento combinado esté disponible de alguna manera, lo que significaría otro especificador.
Jan Hudec

66
@jramoyo: en C #, protectedes solo de clase y subclase, y internales de toda la biblioteca / paquete. También tiene protected internalcuál es el equivalente a Java protected.
Bobson

@Bobson - gracias, la implementación de C # parece una mejor opción
jramoyo

5

En mi humilde opinión, esta fue una mala decisión de diseño en Java.

Solo especulando, pero creo que querían que los niveles de acceso estuvieran en una progresión estricta: privado - "paquete" - protegido - público. No querían tener una jerarquía, donde algunos campos estarían disponibles para el paquete pero no las subclases, algunos disponibles para las subclases pero no el paquete, y algunos ambos.

Aún así, en mi humilde opinión, deberían haber ido para otro lado: Dijo que protegido es visible solo para la clase y las subclases, y que el paquete es visible para la clase, las subclases y el paquete.

A menudo tengo datos en una clase que deben ser accesibles para las subclases, pero no para el resto del paquete. Me cuesta pensar en un caso en el que lo contrario sea cierto. He tenido algunas raras ocasiones en las que he tenido un conjunto de clases interrelacionadas que necesitan compartir datos pero que deberían mantener esos datos seguros fuera del paquete. Así que está bien, póngalos en un paquete, etc. Pero nunca he tenido un caso en el que quiera que el paquete comparta datos, pero quiero mantenerlo fuera de las subclases. De acuerdo, me puedo imaginar la situación que se avecina. Supongo que si este paquete es parte de una biblioteca que podría extenderse por clases de las que no sé nada, por las mismas razones que haría que los datos de una clase fueran privados. Pero es mucho más común querer que los datos estén disponibles solo para la clase y sus hijos.

Hay muchas cosas que mantengo en privado entre mis hijos y yo y que no compartimos con los vecinos. Es muy poco lo que mantengo en privado entre mí y los vecinos y no comparto con mis hijos. :-)


0

Un buen ejemplo que viene a la mente de inmediato son las clases de utilidad que se usan mucho en el paquete pero que no desea acceso público (detrás de escena, carga de imágenes desde el disco, clases de creación / destrucción de identificadores, etc. .) en lugar de hacer que todo [el equivalente de Java friend access modifier or idiomen C++] de todas las demás clases esté automáticamente disponible.


1
Las clases de utilidad son más un ejemplo de clases que son internas en su conjunto que sus miembros.
Jan Hudec

0

Los casos de uso para el modificador de acceso protegido / paquete son similares a los de los modificadores de acceso amigo en C ++.

Un caso de uso es cuando se implementa el patrón Memento .

El objeto de recuerdo necesita acceder al estado interno de un objeto, retenerlo, para servir como punto de control para operaciones de deshacer.

Declarar la clase en el mismo paquete es una de las formas posibles de lograr el patrón Memento, ya que Java no tiene un modificador de acceso "amigo" .


1
No, el objeto de recuerdo no necesita y no debe tener acceso al objeto. Es el objeto que se serializa en el recuerdo y se deserializa nuevamente. Y el recuerdo en sí mismo es solo una bolsa de propiedades tontas. Ninguno de los dos debería tener más que acceso público al otro.
Jan Hudec

@JanHudec dije literalmente "es -> una <- de las posibles formas de lograr el patrón Memento" .
Tulains Córdova

Y otra forma posible de lograr el patrón Memento es hacer que todo sea público. Es decir, no entiendo tu punto.
Thomas Eding

-1

¿simetría?

Es raro necesitar dicho acceso, por lo que el acceso predeterminado rara vez se usa. Pero a veces los marcos lo quieren para el código generado, donde las clases de contenedor se colocan en el mismo paquete que interactúa directamente con sus clases, yendo a los miembros en lugar de a los accesores por razones de rendimiento. Piense en GWT.


1
gracias, tienes un ejemplo? suena como que pueden llevarse a cabo por un "defecto" de acceso en lugar de "protegido"
jramoyo

-1

La jerarquía de encapsulación de Java está bien definida:

Clase -> Paquete -> Herencia

"Protegido" es solo una forma más débil de privacidad que el paquete predeterminado, según lo decidido por los diseñadores de Java. El acceso a los elementos predeterminados del paquete está restringido a un subconjunto de las entidades que pueden acceder a los elementos protegidos.

Tiene mucho sentido desde un punto de vista matemático y de implementación tener sus conjuntos de entidades que tienen acceso a cosas para anidar. (No puede anidar su conjunto de acceso a paquetes dentro de su conjunto de acceso protegido porque las clases pueden heredar de otros paquetes).

Tiene sentido desde un punto de vista conceptual tener algo en java.util para ser "más amigable" con otra clase en el paquete que algo en com.example.foo.bar que lo está subclasificando. En el primer caso, es probable que las clases sean escritas por el mismo autor, o al menos codificadores de la misma organización.


1
¿Qué significa "solo una forma más débil ..." ?
mosquito
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.