Por que não devemos usar estática protegida em java


121

Eu estava passando por essa pergunta Existe uma maneira de substituir variáveis ​​de classe em Java? O primeiro comentário com 36 votos positivos foi:

Se você vir um protected static, corra.

Alguém pode explicar por que é protected staticdesaprovado?


6
Não há nada de errado com um campo estático protegido, desde que seja final. Um campo estático mutável compartilhado entre classes é definitivamente motivo de preocupação. É provável que várias classes que atualizam um campo estático não sejam confiáveis ​​ou fáceis de seguir, especialmente porque a presença de qualquer campo ou método protegido implica que a classe deve ser estendida por classes em outros pacotes, possivelmente classes que não estão sob o controle do autor da classe que contém o campo protegido.
VGR 18/06

6
@VGR, finalnão significa que o campo é imutável. Você sempre pode modificar o objectreferenciado por uma finalvariável de referência.
precisa

@VGR Eu discordo. A única razão pela qual você criaria uma variável estática é ter acesso a ela de outro pacote apenas por herança, e o acesso a um único campo NÃO deve ser o motivo da herança. É um design defeituoso, IMO, e se você recorrer a isso, provavelmente deverá repensar a estrutura do seu aplicativo. Isso é só minha opinião.
Dioxina

@LoneRider Você está certo. Eu estava pensando imutável, e final certamente não garante isso.
VGR 18/06

Até eu vim aqui da mesma pergunta.
Raj Rajeshwar Singh Rathore

Respostas:


86

É mais uma coisa estilística do que um problema direto. Isso sugere que você não pensou adequadamente no que está acontecendo com a classe.

Pense no que staticsignifica:

Essa variável existe no nível da classe, não existe separadamente para cada instância e não existe uma existência independente nas classes que me estendem .

Pense no que protectedsignifica:

Essa variável pode ser vista por essa classe, classes no mesmo pacote e classes que me estendem .

Os dois significados não são exatamente mutuamente exclusivos, mas são bem próximos.

O único caso em que posso ver onde você pode usar os dois juntos é se você tivesse uma classe abstrata que foi projetada para ser estendida e a classe estendida pudesse modificar o comportamento usando constantes definidas no original. Esse tipo de arranjo provavelmente acabaria muito confuso e indica fraqueza no design das classes.

Na maioria dos casos, seria melhor ter as constantes como públicas, pois isso apenas torna tudo mais limpo e permite que as pessoas subclasses tenham mais flexibilidade. Independentemente de qualquer outra coisa, em muitos casos, a composição é preferível à herança, enquanto as classes abstratas forçam a herança.

Para ver um exemplo de como isso pode quebrar as coisas e ilustrar o que quero dizer com a variável que não possui existência independente, tente este código de exemplo:

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;
    }
}

Você verá os resultados:

test
changed

Experimente você mesmo em: https://ideone.com/KM8u8O

A classe Test2é capaz de acessar o membro estático testpartir Testsem a necessidade de qualificar o nome - mas não herda ou obter sua própria cópia. Ele está olhando exatamente para o mesmo objeto na memória.


4
Vocês estão pendurados na herança. O caso típico em que isso ocorre é alguém que deseja acessar o pacote sem torná-lo público (por exemplo, um teste de unidade).
Spudone

3
@ spudone, mas os testes de unidade geralmente são colocados na mesma embalagem. Para dar acesso, basta usar o nível de acesso padrão (pacote). Protected também dá acesso a subclasses que não são necessárias ou relevantes para o teste de unidade.
Tim B

1
@Kamafeather Protected significa que as crianças podem vê-lo de diferentes classes e quaisquer classes do mesmo pacote podem vê-lo. O programa está no mesmo pacote e, portanto, pode ver o membro protegido.
Tim B

2
" a classe estendida poderia modificar o comportamento " Isso violaria o princípio da Subscrição Liskov. Uma subclasse não deve modificar o comportamento de seu supertipo. Isso se enquadra no argumento "um quadrado não é um retângulo": ajustar a superclasse ( Rectangle) para ajustar a largura / altura para garantir que a igualdade Squareproduzirá resultados indesejados se você substituir todos os supertipos por uma instância de seu subtipo.
Dioxin 03/03

1
" O único caso que eu posso ver onde você pode usar os dois juntos é se você tivesse uma classe abstrata que foi projetada para ser estendida e a classe estendida pudesse modificar o comportamento usando constantes definidas no original " Um pouco oculto, mas está dentro há. O exemplo de código também expressa a declaração
Dioxin

32

É desaprovado porque é contraditório.

Criar uma variável protectedimplica que ela será usada dentro do pacote ou será herdada dentro de uma subclasse .

Tornar a variável o statictorna um membro da classe, eliminando as intenções de herdá-la . Isso deixa apenas a intenção de ser usado dentro de um pacote , e temos package-privatepara isso (nenhum modificador).

A única situação para a qual eu poderia achar útil é se você estivesse declarando uma classe que deveria ser usada para iniciar o aplicativo (como o JavaFX Application#launche apenas queria poder iniciar a partir de uma subclasse. Se fizer isso, garanta que o método também finalseja disallow esconder . Mas este não é "a norma", e provavelmente foi implementada para evitar a adição de mais complexidade adicionando uma nova maneira de iniciar aplicativos.

Para ver os níveis de acesso de cada modificador, consulte: Os tutoriais em Java - controlando o acesso aos membros de uma classe


4
Não entendo como staticeliminaria as intenções de herdá-lo. Porque minha subclasse em outro pacote ainda exige que o campo super seja protectedacessado, mesmo que seja static. package-privatenão pode ajudar
Aobo Yang

@AoboYang Você está certo, e é por isso que algumas pessoas usam protected static. Mas é um cheiro de código, daí a parte " executar ". Modificadores de acesso e herança são dois assuntos diferentes. Sim, você não poderá acessar um membro estático de uma superclasse, se fosse package-private. Mas você não deve confiar na herança para referenciar os staticcampos em primeiro lugar; é um sinal de mau design. Você notará que tentativas de substituir staticmétodos não dão resultados, o que é um sinal claro de que a herança não é baseada em classe. Se você precisar de fora o acesso da classe ou pacote, ele deve serpublic
Dioxina

3
Eu tenho uma classe com algumas private staticfunções util no início, mas acho que alguém pode querer melhorar ou personalizar da minha classe e essas funções util também podem fornecer comodidade para elas. publicpode não ser apropriado, pois os métodos util não são para o usuário das instâncias da minha classe. Você poderia me ajudar a descobrir um bom design em vez de protected? Obrigado
Aobo Yang

Nesse link, ele diz 'Use o nível de acesso mais restritivo que faça sentido para um membro específico. Use privado, a menos que tenha um bom motivo para não fazê-lo. , o que está em contradição com esta pergunta, algum pensamento?
Cecilia

13

Não vejo uma razão específica para que isso deva ser desaprovado. Sempre pode haver alternativas para alcançar o mesmo comportamento, e dependerá da arquitetura real se essas alternativas são "melhores" do que um método estático protegido ou não. Mas um exemplo em que um método estático protegido seria razoável, pelo menos, poderia ser o seguinte:

(Editado para dividir em pacotes separados, para facilitar o uso protected)

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 disso:

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;
    }
}

Outra classe 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;
    }
}

O protected staticmodificador certamente pode ser justificado aqui:

  • Os métodos podem ser static, porque eles não dependem de variáveis ​​de instância. Eles não devem ser usados ​​diretamente como um método polimórfico, mas são métodos de "utilidade" que oferecem implementações padrão que fazem parte de uma computação mais complexa e servem como "blocos de construção" da implementação real.
  • Os métodos não devem ser public, porque são um detalhe de implementação. E eles não podem ser privateporque devem ser chamados pelas classes que se estendem. Eles também não podem ter visibilidade "padrão", porque não estarão acessíveis para as classes estendidas em outros pacotes.

(EDIT: Pode-se supor que o comentário original se refere apenas a campos , e não a métodos - então, no entanto, era muito geral)


Nesse caso, você deve definir as implementações padrão como protected final(já que você não deseja que elas sejam substituídas), não static. O fato de um método não usar variáveis ​​de instância não significa que deveria ser static(embora possa ).
barfuin

2
@ Thomas Claro, neste caso, isso também seria possível. Em geral: isso certamente é parcialmente subjetivo, mas minha regra geral é: quando um método não se destina a ser usado polimorficamente, e pode ser estático, eu o torno estático. Ao contrário de finaldeixar claro, não apenas deixa claro que o método não deve ser substituído, mas também deixa claro ao leitor que o método não usa variáveis ​​de instância. Tão sucintamente: simplesmente não há razão para não torná-lo estático.
Marco13

1
Quando um método não se destina a ser usado polimorficamente, e pode ser estático, eu o torno estático. - Isso causará problemas quando você começar a usar estruturas simuladas para testes de unidade. Mas isso nos leva a um tópico diferente ...
barfuin

@ Marco13 também ocorre quando você faz algo protected, não para mostrar herança, mas para mantê-lo em um único pacote - não exposto. Eu acho que as interfaces selados se tornaria útil dessa maneira quando eles vão em java
Eugene

@ Eugene Esse comentário me irrita um pouco. A visibilidade do pacote pode ser alcançada sem nenhum modificador, ao passo que protectedsignifica "Herdando classes (mesmo em pacotes diferentes)" ...
Marco13:

7

Membros estáticos não são herdados e membros protegidos são visíveis apenas para subclasses (e, claro, a classe que os contém), portanto, a protected statictem a mesma visibilidade que static, sugerindo um mal-entendido pelo codificador.


1
Você poderia fornecer uma fonte para sua primeira declaração? Em um teste rápido, um int estático protegido foi herdado e reutilizado em uma subclasse sem nenhum problema.
hiergiltdiestfu

A estática protegida tem a mesma visibilidade que a estática privada do pacote, não estática privada. Para adicionar isso, se você estiver usando protected e static, é melhor apenas remover o modificador de acesso para torná-lo privado do pacote (se sua intenção fosse torná-lo acessível dentro do pacote)
Dioxin

2
Uhm ... não. pacote privado e protectednão são os mesmos. Se você acabou de dizer static, o campo é visível apenas para subclasses no mesmo pacote.
Aaron Digulla

1
O @AaronDigulla protected staticpermite acesso dentro do pacote ou de uma subclasse . Tornar uma variável estática remove as intenções de subclassificação, deixando a única intenção de acessar dentro do pacote.
Dioxin

@VinceEmigh: Desculpe, eu estava conversando com a Bohemian. Deveria ter deixado isso mais claro.
Aaron Digulla

5

Na verdade, não há nada de fundamentalmente errado protected static. Se você realmente deseja uma variável estática ou método visível para o pacote e todas as subclasses da classe declarante, vá em frente e faça isso protected static.

Algumas pessoas geralmente evitam usar protectedpor várias razões e outras acham que staticvariáveis não finais devem ser evitadas por todos os meios (eu simpatizo pessoalmente com essa última até certo ponto), então acho que a combinação protectede staticdeve parecer ruim 2 para aquelas que pertencem a ambos os grupos.


3

Protected é usado para que possa ser usado em subclasses. Não há lógica na definição de uma estática protegida ao usar no contexto de classes concretas, pois você pode acessar a mesma variável é uma maneira estática. No entanto, o complier emitirá um aviso para acessar a variável estática da superclasse de maneira estática.


1

Bem, como a maioria das pessoas respondeu:

  • protectedsignifica - ' pacote-privado + visibilidade para subclasses - a propriedade / comportamento é herdado '
  • staticsignifica - ' o oposto da instância - é uma propriedade / comportamento de CLASS, ou seja, NÃO É HERDADO '

Portanto, eles são um pouco contraditórios e incompatíveis.

No entanto, recentemente cheguei a um caso de uso em que poderia fazer sentido usar esses dois juntos. Imagine que você deseja criar uma abstractclasse que é pai de tipos imutáveis e que possui várias propriedades comuns aos subtipos. Para implementar a imutabilidade corretamente e manter a legibilidade, pode-se decidir usar o padrão 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;
    }
}

E por que protected static? Porque eu quero um subtipo não abstrato do AbstactTypequal implemente seu próprio construtor não abstrato e esteja localizado fora package Xpara poder acessar e reutilizar o 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;
    }
}

Claro que podemos tornar BaseBuilderpúblico, mas depois chegamos a outras declarações contraditórias:

  • Temos uma classe não instanciada (resumo)
  • Nós fornecemos um construtor público para isso

Assim, nos dois casos com protected statice publicconstrutor de umabstract class , combinamos declarações contraditórias. É uma questão de preferências pessoais.

No entanto, eu ainda prefiro o publiccriador de umabstract class porque o protected staticpara mim parece mais artificial em um mundo OOD e OOP!


0

Não há nada errado em ter protected static. Uma coisa que muitas pessoas ignoram é que você pode escrever casos de teste para métodos estáticos que não deseja expor em circunstâncias normais. Percebi que isso é particularmente útil para escrever testes para o método estático nas classes de utilidade.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.