Em Java, por que os membros protegidos foram disponibilizados para classes do mesmo pacote?


14

A partir da documentação oficial ...

Mundo da subclasse do pacote da classe modificadora 
AAAA público 
YYYN protegido 
sem modificador YYNN 
YNNN privado 

O problema é que não me lembro de ter um caso de uso em que eu precisava acessar membros protegidos de uma classe dentro do mesmo pacote.

Quais foram as razões por trás dessa implementação?

Editar: para esclarecer, estou procurando um caso de uso específico em que uma subclasse e uma classe dentro do mesmo pacote precisam acessar um campo ou 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();
 }
}

Respostas:


4

procurando um caso de uso específico em que uma subclasse e uma classe dentro do mesmo pacote precisem acessar um campo ou método protegido ...

Bem, para mim, esse caso de uso é mais geral do que específico e deriva das minhas preferências:

  1. Comece com o modificador de acesso o mais rigoroso possível, recorrendo a um (s) mais fraco (s) somente mais tarde, conforme necessário.
  2. Os testes de unidade residem no mesmo pacote que o código testado.

Acima, eu posso começar a projetar para meus objetos com modificadores de acesso padrão (eu começaria, privatemas isso complicaria o teste de unidade):

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 lateral no trecho acima, Unit1, Unit2e UnitTestsão aninhados dentro Examplede simplicidade de apresentação, mas em um projeto real, eu provavelmente teria essas classes em arquivos separados (e UnitTestaté mesmo em um diretório separado ).

Então, quando surgir uma necessidade, eu enfraqueceria o controle de acesso do padrão para 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();
        }
    }
}

Veja bem, eu posso manter o código de teste de unidade ExampleEvolvedinalterado devido ao acesso a métodos protegidos do mesmo pacote, mesmo que acessar objeto não seja uma subclasse .

Menos mudanças necessárias => modificação mais segura; afinal, mudei apenas os modificadores de acesso e não modifiquei os métodos Unit1.doSomething()e o que Unit2.doSomething()faço, por isso é natural esperar que o código de teste de unidade continue sendo executado sem modificações.


5

Eu diria que isso tem duas partes:

  1. O acesso padrão, "pacote", é útil em diversos casos, porque as classes nem sempre são boas unidades de encapsulamento. Vários objetos compostos em que algum objeto atua como coleção de outros objetos, mas os itens não devem ser publicamente modificáveis, porque existem alguns invariantes em toda a coleção, portanto, a coleção precisa ter acesso elevado aos itens. C ++ tem amigos, Java tem acesso a pacotes.
  2. Agora, o escopo de acesso do "pacote" é basicamente independente do escopo da "subclasse" (protegido). Portanto, você precisaria de especificadores de acesso adicionais apenas para pacotes, apenas subclasses e package e subclasses. O escopo "pacote" é mais restrito, pois o conjunto de classes em um pacote geralmente é definido enquanto uma subclasse pode aparecer em qualquer lugar. Portanto, para simplificar, o Java simplesmente inclui o acesso ao pacote no acesso protegido e não possui um especificador extra para subclasses-mas-não-pacote. Embora você quase sempre deva pensar protectedexatamente nisso.

Não seria mais simples se protectedé apenas subclasse? Honestamente, há muito tempo, eu estava sob a impressão de que esse é o comportamento
jramoyo

@jramoyo: Não, porque você ainda precisaria disponibilizar o comportamento combinado de alguma forma, o que significaria outro especificador.
Jan Hudec

6
@jramoyo - Em C #, protectedé apenas de classe e subclasse e internalé de toda a biblioteca / pacote. Ele também possui protected internalo equivalente ao Java protected.
22413 Bobson

@Bobson - graças, o C # implementação parece ser uma escolha melhor
jramoyo

5

IMHO, essa foi uma má decisão de design em Java.

Apenas especulando, mas acho que eles queriam que os níveis de acesso estivessem em uma progressão estrita: privado - "pacote" - protegido - público. Eles não queriam ter uma hierarquia, onde alguns campos estariam disponíveis para o pacote, mas não as subclasses, alguns disponíveis para as subclasses, mas não o pacote, e alguns ambos.

Ainda assim, na minha humilde opinião, eles deveriam ter seguido o contrário: Disse que protected é visível apenas para a classe e subclasses, e esse pacote é visível para a classe, subclasses e pacote.

Costumo ter dados em uma classe que precisa estar acessível para subclasses, mas não para o restante do pacote. Estou com dificuldade para pensar em um caso em que o inverso era verdadeiro. Tive algumas raras ocasiões em que tive um pacote de classes inter-relacionadas que precisam compartilhar dados, mas que devem mantê-los protegidos de fora do pacote. Tudo bem, coloque-os em um pacote, etc. Mas nunca tive um caso em que quero que o pacote compartilhe dados, mas quero mantê-lo de subclasses. Ok, eu posso imaginar a situação chegando. Suponho que se esse pacote fizer parte de uma biblioteca que possa ser estendida por classes que eu não conheço, pelas mesmas razões pelas quais tornaria os dados em uma classe privados. Mas é muito mais comum querer disponibilizar dados apenas para a classe e seus filhos.

Há muitas coisas que mantenho em segredo entre mim e meus filhos e não compartilhamos com os vizinhos. Há muito pouco que eu mantenho em segredo entre mim e os vizinhos e não compartilho com meus filhos. :-)


0

Um bom exemplo que vem imediatamente à mente são as classes de utilitários que são muito usadas no pacote, mas você não deseja acesso público (classes de carregamento de imagem nos bastidores, classes de criação / destruição dos bastidores, etc.) .) em vez de disponibilizar tudo [equivalente a Java friend access modifier or idiomem C++] de todas as outras classes, tudo fica automaticamente disponível.


1
As classes de utilidade são mais um exemplo de classes que são internas como um todo e não seus membros.
Jan Hudec

0

Os casos de uso do modificador de acesso protegido / pacote são semelhantes aos dos modificadores de acesso a amigos em C ++.

Um caso de uso é ao implementar o padrão Memento .

O objeto de lembrança precisa acessar o estado interno de um objeto, mantê-lo, para servir como um ponto de verificação para operações de desfazer.

Declarar a classe no mesmo pacote é uma das maneiras possíveis de obter o padrão Memento, pois o Java não possui modificador de acesso "amigo" .


1
Não, o objeto memento não precisa e não deve ter acesso ao objeto. É o objeto que se serializa na lembrança e desserializa novamente. E a lembrança em si é apenas uma sacola idiota da propriedade. Nenhum dos dois deve ter mais do que acesso público ao outro.
Jan Hudec

@JanHudec afirmei literalmente "é -> uma <- das maneiras possíveis de alcançar o padrão Memento" .
Tulains Córdova

E ainda outra maneira possível de alcançar o padrão Memento é tornar tudo público. Ou seja, não entendi o seu ponto.
Thomas Eding

-1

simetria?

É raro precisar desse acesso, e é por isso que o acesso padrão é tão raramente usado. Às vezes, porém, as estruturas desejam o código gerado, onde as classes de wrapper são colocadas no mesmo pacote que interage diretamente com suas classes, acessando membros em vez de acessadores por razões de desempenho. Pense no GWT.


1
obrigado, você tem um exemplo? soa como que pode ser realizado por um "default" acessor em vez de "protegido"
jramoyo

-1

A hierarquia de encapsulamento do Java está bem definida:

Classe -> Pacote -> Herança

"Protected" é apenas uma forma mais fraca de privacidade do que o padrão do pacote, conforme decidido pelos designers de Java. O acesso aos itens padrão do pacote é restrito a um subconjunto das entidades que têm permissão para acessar itens protegidos.

Do ponto de vista matemático e de implementação, faz muito sentido que seus conjuntos de entidades com permissão para acessar itens sejam aninhados. (Você não pode aninhar seu conjunto de acesso a pacotes dentro do conjunto de acesso protegido porque as classes têm permissão para herdar de outros pacotes).

Do ponto de vista conceitual, faz sentido que algo no java.util seja "mais amigável" com outra classe no pacote do que algo no com.example.foo.bar que a está subclassificando. No primeiro caso, as classes provavelmente serão escritas pelo mesmo autor, ou pelo menos codificadores da mesma organização.


1
o que significa "apenas uma forma mais fraca ..." ?
Gnat
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.