Por que no deberíamos usar estática protegida en java

122

Estaba pasando por esta pregunta ¿Hay alguna forma de anular las variables de clase en Java? El primer comentario con 36 votos a favor fue:

Si alguna vez ve un protected static, ejecute.

¿Alguien puede explicar por qué está protected staticmal visto?

Zeeshan
fuente
6
No hay nada de malo en un campo estático protegido, siempre que lo sea final. Un campo estático mutable compartido entre clases es definitivamente motivo de preocupación. Es probable que varias clases que actualizan un campo estático no sean confiables o fáciles de seguir, especialmente porque la presencia de cualquier campo o método protegido implica que la clase está destinada a ser extendida por clases en otros paquetes, posiblemente clases que no están bajo el control del autor de la clase que contiene el campo protegido.
VGR
6
@VGR, finalno significa que el campo sea inmutable. Siempre puede modificar el objectreferenciado por una finalvariable de referencia.
Zeeshan
@VGR No estoy de acuerdo. La ÚNICA razón por la que haría una variable estática es para tener acceso a ella desde otro paquete solo por herencia, y el acceso a un solo campo NO debería ser la razón de la herencia. Es un diseño defectuoso, en mi opinión, y si recurre a eso, probablemente debería reconsiderar la estructura de su aplicación. Aunque esa es sólo mi opinión.
Dioxina
@LoneRider Tienes razón. Estaba pensando inmutable, y el final ciertamente no garantiza eso.
VGR
Incluso yo vine aquí por la misma pregunta.
Raj Rajeshwar Singh Rathore

Respuestas:

86

Es más una cuestión de estilo que un problema directo. Sugiere que no ha pensado adecuadamente en lo que está sucediendo con la clase.

Pensar en qué static significa:

Esta variable existe a nivel de clase, no existe por separado para cada instancia y no tiene una existencia independiente en las clases que me extienden .

Pensar en qué protected significa:

Esta variable puede ser vista por esta clase, clases en el mismo paquete y clases que me extienden .

Los dos significados no son exactamente excluyentes entre sí, pero están bastante cerca.

El único caso que puedo ver en el que podría usar los dos juntos es si tuviera una clase abstracta que fue diseñada para ser extendida y la clase extensible podría modificar el comportamiento usando constantes definidas en el original. Sin embargo, ese tipo de arreglo probablemente terminaría muy desordenado e indica una debilidad en el diseño de las clases.

En la mayoría de los casos, sería mejor tener las constantes como públicas, ya que eso solo hace que todo sea más limpio y permite a las personas que subclasifican más flexibilidad. Aparte de cualquier otra cosa, en muchos casos la composición es preferible a la herencia, mientras que las clases abstractas fuerzan la herencia.

Para ver un ejemplo de cómo esto podría romper las cosas y para ilustrar lo que quiero decir con la variable que no tiene una existencia independiente, pruebe este código de ejemplo:

public class Program {
    public static void main (String[] args) throws java.lang.Exception {
        System.out.println(new Test2().getTest());
        Test.test = "changed";
        System.out.println(new Test2().getTest());
    }
}

abstract class Test {
    protected static String test = "test";
}

class Test2 extends Test {
    public String getTest() {
        return test;
    }
}

Verás los resultados:

test
changed

Pruébelo usted mismo en: https://ideone.com/KM8u8O

La clase Test2puede acceder al miembro estático testdesde Testsin necesidad de calificar el nombre, pero no hereda ni obtiene su propia copia. Está mirando exactamente el mismo objeto en la memoria.

Tim B
fuente
4
Ustedes están colgados de la herencia. El caso típico en el que se ve esto es alguien que desea acceder al paquete sin hacerlo público (por ejemplo, una prueba unitaria).
spudone
3
@spudone, pero las pruebas unitarias generalmente se colocan en el mismo paquete. Para darles acceso, simplemente use el nivel de acceso predeterminado (paquete). Protegido también da acceso a subclases que no son requeridas o relevantes para la prueba unitaria.
Tim B
1
@Kamafeather Protected significa que los niños pueden verte de diferentes clases y cualquier clase del mismo paquete puede verte. Entonces, sí, el programa está en el mismo paquete y, por lo tanto, puede ver el miembro protegido.
Tim B
2
" la clase que se extiende podría modificar el comportamiento ". Esto violaría el principio de sustitución de Liskov. Una subclase no debería modificar el comportamiento de su supertipo. Esto se incluye en el argumento completo de "un cuadrado no es un rectángulo": ajustar la superclase ( Rectangle) para ajustar el ancho / alto para garantizar la igualdad (para Square) producirá resultados no deseados si reemplaza cada supertipo con una instancia de su subtipo.
Dioxina
1
" El único caso que puedo ver en el que podría usar los dos juntos es si tuviera una clase abstracta que fue diseñada para ser extendida y la clase extensible podría modificar el comportamiento usando constantes definidas en el original " Un poco oculto, pero está en ahí. El ejemplo de código también expresa la declaración
Dioxina
32

Está mal visto porque es contradictorio.

Hacer una variable protectedimplica que se usará dentro del paquete o se heredará dentro de una subclase .

Hacer la variable la staticconvierte en miembro de la clase, eliminando las intenciones de heredarla . Esto deja solo la intención de ser utilizado dentro de un paquete , y tenemos package-privatepara eso (sin modificador).

La única situación para la que podría encontrar esto útil es si estuviera declarando una clase que debería usarse para iniciar la aplicación (como JavaFX Application#launch, y solo quisiera poder iniciar desde una subclase. Si lo hace, asegúrese de que el método también sea finalpara no permitir la ocultación . Pero esto no es "la norma", y probablemente se implementó para evitar agregar más complejidad al agregar una nueva forma de iniciar aplicaciones.

Para ver los niveles de acceso de cada modificador, vea esto: Los tutoriales de Java: control del acceso a los miembros de una clase

Dioxina
fuente
4
No entiendo cómo staticeliminaría las intenciones de heredarlo. Debido a que mi subclase en otro paquete aún requiere que el campo de super sea protectedpara acceder, aunque sea static. package-privateno puedo ayudar
Aobo Yang
@AoboYang Tienes razón, por eso algunas personas usan protected static. Pero es un olor a código, de ahí la parte de " ejecución ". Los modificadores de acceso y la herencia son dos temas diferentes. Sí, no podrá acceder a un miembro estático de una superclase si lo fuera package-private. Pero no debe confiar en la herencia para hacer referencia a los staticcampos en primer lugar; es una señal de mal diseño. Notará que los intentos de anular staticmétodos no dan resultados, lo que es una clara señal de que la herencia no se basa en clases. Si necesita acceso fuera de la clase o paquete, debería serpublic
Dioxina
3
Tengo una clase con algunas private staticfunciones útiles al principio, pero creo que alguien puede querer mejorar o personalizar mi clase y estas funciones útiles también pueden brindarles conveniencia. publicpuede no ser apropiado ya que los métodos util no son para el usuario de las instancias de mi clase. ¿Podrías ayudarme a encontrar un buen diseño en lugar de protected? Gracias
Aobo Yang
En ese enlace, dice 'Use el nivel de acceso más restrictivo que tenga sentido para un miembro en particular. Use privado a menos que tenga una buena razón para no hacerlo. , que contradice esta pregunta, ¿algún pensamiento?
Cecilia
13

No veo una razón en particular por la que esto deba estar mal visto. Siempre puede haber alternativas para lograr el mismo comportamiento, y dependerá de la arquitectura real si estas alternativas son "mejores" que un método estático protegido o no. Pero un ejemplo en el que un método estático protegido sería razonable, al menos, podría ser el siguiente:

(Editado para dividir en paquetes separados, para hacer el uso protectedmás claro)

package a;
import java.util.List;

public abstract class BaseClass
{
    public Integer compute(List<Integer> list)
    {
        return computeDefaultA(list)+computeDefaultB(list);
    }

    protected static Integer computeDefaultA(List<Integer> list)
    {
        return 12;
    }
    protected static Integer computeDefaultB(List<Integer> list)
    {
        return 34;
    }
}

Derivado de eso:

package a.b;

import java.util.List;

import a.BaseClass;

abstract class ExtendingClassA extends BaseClass
{
    @Override
    public Integer compute(List<Integer> list)
    {
        return computeDefaultA(list)+computeOwnB(list);
    }

    private static Integer computeOwnB(List<Integer> list)
    {
        return 56;
    }
}

Otra clase derivada:

package a.b;

import java.util.List;

import a.BaseClass;

abstract class ExtendingClassB extends BaseClass
{
    @Override
    public Integer compute(List<Integer> list)
    {
        return computeOwnA(list)+computeDefaultB(list);
    }

    private static Integer computeOwnA(List<Integer> list)
    {
        return 78;
    }
}

El protected staticmodificador ciertamente se puede justificar aquí:

  • Los métodos pueden serlo static, porque no dependen de variables de instancia. No están destinados a ser utilizados directamente como un método polimórfico, sino que son métodos de "utilidad" que ofrecen implementaciones predeterminadas. que son parte de un cálculo más complejo y sirven como "bloques de construcción" de la implementación real.
  • Los métodos no deberían serlo public, porque son un detalle de implementación. Y no pueden ser privateporque deberían ser llamados por las clases extendidas. Tampoco pueden tener visibilidad "predeterminada", porque entonces no serán accesibles para las clases extendidas en otros paquetes.

(EDITAR: Se podría suponer que el comentario original solo se refería a campos , y no a métodos ; entonces, sin embargo, era demasiado general)

Marco13
fuente
En este caso, debe definir las implementaciones predeterminadas como protected final(ya que no desea que se anulen), no static. El hecho de que un método no utilice variables de instancia no significa que deba hacerlo static(aunque puede hacerlo ).
Barfuin
2
@Thomas Claro, en este caso, esto también sería posible. En general: esto ciertamente es parcialmente subjetivo, pero mi regla general es: cuando un método no está destinado a ser utilizado polimórficamente y puede ser estático, entonces lo hago estático. A diferencia de hacerlo final, no solo deja en claro que el método no está destinado a ser anulado, sino que además le aclara al lector que el método no usa variables de instancia. En resumen: simplemente no hay razón para no hacerlo estático.
Marco13
1
Cuando un método no está destinado a ser utilizado de forma polimórfica, y puede ser estático, lo hago estático. - Esto te meterá en problemas cuando empieces a usar marcos simulados para pruebas unitarias. Pero esto nos lleva a un tema diferente ...
barfuin
@ Marco13 también existe el caso cuando crea algo protected, no para mostrar la herencia, sino para mantenerlo en un solo paquete, no expuesto. Supongo que las interfaces selladas serían útiles de esta manera cuando lo hagan en Java
Eugene
@Eugene Ese comentario me irrita un poco. La visibilidad del paquete se puede lograr sin ningún modificador, mientras que protectedsignifica "Heredar clases (incluso en paquetes diferentes)" ...
Marco13
7

Los miembros estáticos no se heredan y los miembros protegidos solo son visibles para las subclases (y, por supuesto, la clase que los contiene), por lo que a protected statictiene la misma visibilidad que static, lo que sugiere un malentendido por parte del codificador.

Bohemio
fuente
1
¿Podría proporcionar una fuente para su primera declaración? En una prueba rápida, se heredó un int estático protegido y se reutilizó en una subclase sin ningún problema.
hiergiltdiestfu
La estática protegida tiene la misma visibilidad que la estática privada de paquete, no la estática privada. Para agregar a esto, si está utilizando protected y static, es mejor eliminar el modificador de acceso para que sea privado del paquete (si sus intenciones fueran que sea accesible dentro del paquete)
Dioxin
2
Uhm ... no. paquete privado y protectedno son lo mismo. Si solo dice static, el campo solo es visible para las subclases del mismo paquete.
Aaron Digulla
1
@AaronDigulla protected staticpermite el acceso dentro del paquete o desde una subclase . Hacer una variable estática elimina las intenciones de subclasificar, dejando que la única intención sea el acceso desde dentro del paquete.
Dioxina
@VinceEmigh: Lo siento, estaba hablando con Bohemian. Debería haber dejado esto más claro.
Aaron Digulla
5

En realidad, no hay nada fundamentalmente malo en ello protected static. Si realmente desea una variable o método estático que sea visible para el paquete y todas las subclases de la clase declarante, entonces hágaloprotected static .

Algunas personas generalmente evitan el uso protectedpor varias razones y algunas personas piensan que las staticvariables no finales deben evitarse por todos los medios (personalmente simpatizo con esta última hasta cierto punto), por lo que supongo que la combinación de protectedy staticdebe quedar mal ^ 2 con las que pertenecen a ambos grupos.

x4u
fuente
3

Protegido se usa para que pueda usarse en subclases. No hay lógica en definir una estática protegida cuando se usa en el contexto de clases concretas, ya que se puede acceder a la misma variable de forma estática, sin embargo, el compilador dará una advertencia para acceder a la variable estática de la superclase de forma estática.

Mithun Kannoth
fuente
1

Bueno, como la mayoría de la gente ha respondido:

  • protectedsignifica - ' paquete-privado + visibilidad a subclases - la propiedad / comportamiento es HEREDADA '
  • staticsignifica - ' lo opuesto a la instancia - es una propiedad / comportamiento de CLASE, es decir, NO ES HEREDADO '

Por tanto, son levemente contradictorios e incompatibles.

Sin embargo, recientemente se me ocurrió un caso de uso en el que podría tener sentido usar estos dos juntos. Imagine que desea crear una abstractclase que sea padre para tipos inmutables y que tenga un montón de propiedades que son comunes a los subtipos. Para implementar la inmutabilidad correctamente y mantener la legibilidad, uno podría decidir usar el patrón Builder .

package X;
public abstract class AbstractType {
    protected Object field1;
    protected Object field2;
    ...
    protected Object fieldN;

    protected static abstract class BaseBuilder<T extends BaseBuilder<T>> {
        private Object field1; // = some default value here
        private Object field2; // = some default value here
        ...
        private Object fieldN; // = some default value here

        public T field1(Object value) { this.field1 = value; return self();}
        public T field2(Object value) { this.field2 = value; return self();}
        ...
        public T fieldN(Object value) { this.fieldN = value; return self();}
        protected abstract T self(); // should always return this;
        public abstract AbstractType build();
    }

    private AbstractType(BaseBuilder<?> b) {
        this.field1 = b.field1;
        this.field2 = b.field2;
        ...
        this.fieldN = b.fieldN;
    }
}

Y por qué protected static? Porque quiero un subtipo no abstracto AbstactTypeque implemente su propio Builder no abstracto y esté ubicado afuera package Xpara poder acceder y reutilizar el BaseBuilder.

package Y;
public MyType1 extends AbstractType {
    private Object filedN1;

    public static class Builder extends AbstractType.BaseBuilder<Builder> {
        private Object fieldN1; // = some default value here

        public Builder fieldN1(Object value) { this.fieldN1 = value; return self();}
        @Override protected Builder self() { return this; }
        @Override public MyType build() { return new MyType(this); }
    }

    private MyType(Builder b) {
        super(b);
        this.fieldN1 = b.fieldN1;
    }
}

Por supuesto que podemos hacer BaseBuilderpúblico pero luego llegamos a otras declaraciones contradictorias:

  • Tenemos una clase no instanciable (resumen)
  • Proporcionamos un constructor público para ello.

Entonces, en ambos casos con protected staticy publicconstructor de unabstract class combinamos declaraciones contradictorias. Es una cuestión de preferencias personales.

Sin embargo, sigo prefiriendo el publicconstructor de unabstract class porque protected staticme parece más antinatural en un mundo OOD y OOP.

egelev
fuente
0

No hay nada de malo en tener protected static. Una cosa que mucha gente está pasando por alto es que es posible que desee escribir casos de prueba para métodos estáticos que no desea exponer en circunstancias normales. He notado que esto es particularmente útil para escribir pruebas para métodos estáticos en clases de utilidad.

Aelphaeis
fuente