Dlaczego nie powinniśmy używać protected static w Javie

Przechodziłem przez to pytanie Czy Jest jakiś sposób na nadpisanie zmiennych klas w Javie? Pierwszy komentarz z 36 głosami był:

Jeśli kiedykolwiek zobaczysz protected static, uciekaj.

Czy ktoś może wyjaśnić dlaczego protected static jest źle widziany?

Author: Community, 2014-06-18

8 answers

To bardziej rzecz stylistyczna niż bezpośredni problem. To sugeruje, że nie przemyślałeś właściwie, co dzieje się z klasą.

Pomyśl o tym, co static znaczy:

Ta zmienna istnieje na poziomie klasy, nie istnieje oddzielnie dla każdej instancji i nie ma niezależnego istnienia w klasach, które rozszerzają mnie .

Pomyśl o tym, co protected oznacza:

Tę zmienną można zobaczyć za pomocą tej klasy, klas w ten sam pakiet i klasy, które rozszerzają mnie .

Te dwa znaczenia nie wykluczają się wzajemnie, ale są dość bliskie.

Jedynym przypadkiem, jaki widzę, gdzie można użyć tych dwóch razem, jest to, że masz klasę abstrakcyjną, która została zaprojektowana do rozszerzenia i klasa rozszerzająca mogłaby zmodyfikować zachowanie za pomocą stałych zdefiniowanych w oryginale. Nawet wtedy jest to bardzo słaby powód, chociaż jak nadal prawie na pewno byłoby lepiej mając stałe jako publiczne. To po prostu sprawia, że wszystko jest czystsze i pozwala ludziom klasyfikować więcej elastyczności.

Aby rozwinąć i wyjaśnić pierwszy punkt-wypróbuj ten przykładowy kod:

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

Zobaczysz wyniki:

test
changed

Spróbuj sam na: https://ideone.com/KM8u8O

Klasa Test2 jest w stanie uzyskać dostęp do statycznego członka test z Test bez konieczności kwalifikowania nazwy - ale nie dziedziczy ani nie otrzymuje własnej kopii. Wygląda dokładnie tak samo zmienna.

 66
Author: Tim B,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-11-13 22:49:31

Jest źle widziany, bo jest sprzeczny.

Utworzenie zmiennej protected oznacza, że będzie ona używana w pakiecie lub będzie dziedziczona w podklasie.

Tworzenie zmiennej static czyni ją członkiem klasy, eliminując intencje dziedziczenia jej. Pozostawia to jedynie zamiar użycia w pakiecie , A do tego mamy package-private (bez modyfikatora).

Jedyna sytuacja, w której mogłem znaleźć to przydatne dla jeśli deklarujesz klasę, która powinna zostać użyta do uruchomienia aplikacji (np. Application#launch JavaFX, i chcesz mieć tylko możliwość uruchomienia z podklasy. Jeśli to zrobisz, upewnij się, że metoda jest również final, Aby uniemożliwić ukrywanie. Ale to nie jest" norma " i prawdopodobnie został zaimplementowany, aby zapobiec dodaniu większej złożoności poprzez dodanie nowego sposobu uruchamiania aplikacji.

Aby zobaczyć poziomy dostępu każdego modyfikatora, zobacz to: samouczki Java-kontrolowanie dostępu do członków Klasa

 26
Author: Vince Emigh,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-08-04 17:04:28

Nie widzę konkretnego powodu, dla którego powinno się to marszczyć brwi. Zawsze mogą istnieć alternatywy do osiągnięcia tego samego zachowania i będzie zależeć od rzeczywistej achitektury, czy te alternatywy są "lepsze" niż chroniona metoda statyczna, czy nie. Ale jednym z przykładów, w którym metoda statyczna chroniona byłaby rozsądna, może być co najmniej następujące:

(edytowane w celu podzielenia na osobne pakiety, aby uczynić korzystanie z protected jaśniejszym)

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

Że:

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

Inna klasa pochodna:

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

Modyfikator protected static z pewnością można tutaj uzasadnić:

  • metody mogą być static, ponieważ nie zależą od zmiennych instancji. Nie są one przeznaczone do bezpośredniego użycia jako metoda polimorficzna, ale raczej są "użytecznymi" metodami, które oferują domyślne implementacje {21]}, które są częścią bardziej złożonych obliczeń i służą jako "bloki" rzeczywistej implementacji.
  • metody powinny nie być public, ponieważ są one szczegółem implementacji. I nie mogą być private, ponieważ powinny być wywoływane przez klasy rozszerzające. Nie mogą też mieć "domyślnej" widoczności, ponieważ wtedy nie będą dostępne dla klas rozszerzających w innych pakietach.

(EDIT: Można by założyć, że oryginalny komentarz odnosił się tylko do pól , a nie do metod - wtedy jednak był zbyt ogólny)

 11
Author: Marco13,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-06-18 15:40:43

Statyczne członkowie nie są dziedziczone, a chronione członkowie są widoczne tylko dla podklas (i oczywiście klasy zawierającej), więc protected static ma taką samą widoczność jak static, co sugeruje nieporozumienie ze strony kodera.

 7
Author: Bohemian,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-06-18 15:21:33

Protected jest używany tak, że może być używany w podklasach. Nie ma logiki w definiowaniu chronionej statyki podczas używania w kontekście konkretnych klas, ponieważ można uzyskać dostęp do tej samej zmiennej jest statyczny sposób.Jednak kompilator da ostrzeżenie, aby uzyskać dostęp do statycznej zmiennej SUPER class w sposób statyczny.

 3
Author: Mithun Kannoth,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-06-18 15:38:51

Właściwie nie ma nic zasadniczo złego w protected static. Jeśli naprawdę chcesz, aby statyczna zmienna lub metoda była widoczna dla pakietu i wszystkich podklas deklarującej klasy, zrób to protected static.

Niektórzy zazwyczaj unikają używania protected z różnych powodów, a niektórzy uważają, że niekończące się static zmienne należy unikać za wszelką cenę (osobiście do pewnego stopnia sympatyzuję z tą ostatnią), więc myślę, że kombinacja protected i static musi wyglądać bad^2 do tych, które należą do obu grup.

 3
Author: x4u,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-06-18 22:44:52

Nie ma nic złego w posiadaniu protected static. Jedną z rzeczy, o których wiele osób zapomina, jest to, że możesz chcieć napisać przypadki testowe metod statycznych, których nie chcesz ujawniać w normalnych okolicznościach. Zauważyłem, że jest to szczególnie przydatne do pisania testów dla metody statycznej w klasach użyteczności.

 0
Author: Aelphaeis,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-07-05 15:34:02

Cóż, jak większość ludzi odpowiedziała:]}

  • protected oznacza - 'pakiet-prywatna + widoczność do podklas-własność/zachowanie jest dziedziczone '
  • static oznacza - 'przeciwieństwo instancji-jest to właściwość/zachowanie klasy, tzn. nie jest dziedziczone '

Dlatego są one nieco sprzeczne i niezgodne.

[16]} jednak ostatnio doszedłem do przypadku użycia, w którym może mieć sens użycie tych dwóch razem. Wyobraź sobie, że chcesz stworzyć abstract klasa, która jest rodzicem dla typów immutable i ma kilka właściwości wspólnych dla podtypów. Aby poprawnie zaimplementować niezmienność i zachować czytelność można zdecydować się na użycie wzorca 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;
    }
}

I dlaczego protected static ? Ponieważ chcę, aby Podtyp nieabstrakt AbstactType, który implementuje swój własny Nieabstraktowy konstruktor i znajduje się na zewnątrz package X, był w stanie dostęp i ponowne wykorzystanie 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;
    }
}

Oczywiście możemy upublicznić BaseBuilder, ale potem dochodzimy do kolejnych sprzecznych stwierdzeń:

  • mamy klasę nie-instancyjną (abstrakcyjną)
  • W 2009 roku firma została założona w 2009 roku.]}

Tak więc w obu przypadkach z protected static oraz public builder of an abstract class łączymy sprzeczne stwierdzenia. To kwestia osobistych preferencji.

Jednak nadal wolę public builder of na abstract class ponieważ protected static dla mnie jest bardziej nienaturalne w świecie OOD i OOP !

 0
Author: egelev,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-09-06 08:29:40