Warum sollten wir in Java keine geschützte statische Aufladung verwenden?


121

Ich habe diese Frage durchgearbeitet. Gibt es eine Möglichkeit, Klassenvariablen in Java zu überschreiben? Der erste Kommentar mit 36 ​​positiven Stimmen war:

Wenn Sie jemals eine sehen protected static, laufen Sie.

Kann jemand erklären, warum ein protected staticverpönt ist?


6
An einem geschützten statischen Feld ist nichts auszusetzen, solange es ist final. Ein veränderbares statisches Feld, das von mehreren Klassen gemeinsam genutzt wird, gibt definitiv Anlass zur Sorge. Es ist wahrscheinlich, dass mehrere Klassen, die ein statisches Feld aktualisieren, nicht zuverlässig oder leicht zu verfolgen sind, insbesondere da das Vorhandensein eines geschützten Felds oder einer geschützten Methode impliziert, dass die Klasse durch Klassen in anderen Paketen erweitert werden soll, möglicherweise Klassen, die nicht unter der Kontrolle von stehen Autor der Klasse, die das geschützte Feld enthält.
VGR

6
@VGR finalbedeutet nicht, dass das Feld unveränderlich ist. Sie können die objectReferenzvariable jederzeit ändern final.
Zeeshan

@VGR Ich bin anderer Meinung. Der EINZIGE Grund, warum Sie eine statische Variable erstellen würden, besteht darin, dass Sie von einem anderen Paket aus nur durch Vererbung darauf zugreifen können. Der Zugriff auf ein einzelnes Feld sollte NICHT der Grund für die Vererbung sein. Es ist ein fehlerhaftes Design, IMO, und wenn Sie darauf zurückgreifen, sollten Sie wahrscheinlich die Struktur Ihrer Anwendung überdenken. Das ist aber nur meine Meinung.
Dioxin

@ LoneRider Du hast recht. Ich dachte unveränderlich, und final garantiert das sicherlich nicht.
VGR

Sogar ich bin aus derselben Frage hierher gekommen.
Raj Rajeshwar Singh Rathore

Antworten:


86

Es ist eher eine stilistische Sache als ein direktes Problem. Es deutet darauf hin, dass Sie nicht richtig überlegt haben, was mit der Klasse los ist.

Überlegen Sie, was staticbedeutet:

Diese Variable existiert auf Klassenebene, existiert nicht für jede Instanz separat und hat keine unabhängige Existenz in Klassen, die mich erweitern .

Überlegen Sie, was protectedbedeutet:

Diese Variable kann von dieser Klasse, Klassen im selben Paket und Klassen, die mich erweitern, gesehen werden .

Die beiden Bedeutungen schließen sich nicht gerade gegenseitig aus, sind aber ziemlich nahe beieinander.

Der einzige Fall, in dem ich sehen kann, wo Sie die beiden zusammen verwenden könnten, ist, wenn Sie eine abstrakte Klasse hatten, die erweitert werden sollte, und die erweiterte Klasse dann das Verhalten mithilfe der im Original definierten Konstanten ändern könnte. Diese Art der Anordnung würde jedoch höchstwahrscheinlich sehr chaotisch enden und auf eine Schwäche im Design der Klassen hinweisen.

In den meisten Fällen wäre es besser, die Konstanten als öffentlich zu haben, da dies nur alles sauberer macht und den Personen, die in Unterklassen unterteilt sind, mehr Flexibilität ermöglicht. Abgesehen von allem anderen ist in vielen Fällen die Komposition der Vererbung vorzuziehen, während abstrakte Klassen die Vererbung erzwingen.

Um ein Beispiel dafür zu sehen, wie dies Dinge zerstören könnte, und um zu veranschaulichen, was ich damit meine, dass die Variable keine unabhängige Existenz hat, versuchen Sie diesen Beispielcode:

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

Sie sehen die Ergebnisse:

test
changed

Probieren Sie es selbst aus unter: https://ideone.com/KM8u8O

Die Klasse kann von ohne Test2Zugriff auf den Namen auf das statische Mitglied zugreifen, erbt jedoch keine eigene Kopie und erhält keine eigene Kopie. Es betrachtet genau dasselbe Objekt im Speicher.testTest


4
Ihr seid auf Erbschaft aufgehängt. Der typische Fall, in dem dies auftritt, ist jemand, der Paketzugriff wünscht, ohne ihn öffentlich zu machen (z. B. ein Komponententest).
Spudone

3
@spudone, aber Unit-Tests werden im Allgemeinen im selben Paket platziert. Um ihnen Zugriff zu gewähren, verwenden Sie einfach die Standardzugriffsebene (Paket). Geschützt ermöglicht auch den Zugriff auf Unterklassen, die für den Komponententest nicht erforderlich oder relevant sind.
Tim B

1
@Kamafeather Protected bedeutet, dass Kinder Sie aus verschiedenen Klassen sehen können und alle Klassen im selben Paket Sie sehen können. Also ja, das Programm befindet sich im selben Paket und kann daher das geschützte Mitglied sehen.
Tim B

2
" Die erweiterte Klasse könnte dann das Verhalten ändern. " Dies würde das Liskov-Subsitutionsprinzip verletzen. Eine Unterklasse sollte das Verhalten ihres Supertyps nicht ändern. Dies fällt unter das gesamte Argument "Ein Quadrat ist kein Rechteck": Wenn Sie die Oberklasse ( Rectangle) anpassen, um Breite / Höhe anzupassen, um sicherzustellen, dass die Gleichheit (für Square) unerwünschte Ergebnisse liefert, wenn Sie jeden Supertyp durch eine Instanz seines Subtyps ersetzen.
Dioxin

1
" Der einzige Fall, in dem ich sehen kann, wo Sie die beiden zusammen verwenden könnten, ist, wenn Sie eine abstrakte Klasse hatten, die erweitert werden sollte, und die erweiterte Klasse dann das Verhalten mithilfe der im Original definierten Konstanten ändern könnte. " Ein bisschen versteckt, aber es ist in Dort. Das Codebeispiel drückt auch die Aussage aus
Dioxin

32

Es ist verpönt, weil es widersprüchlich ist.

Das Erstellen einer Variablen protectedimpliziert, dass sie innerhalb des Pakets verwendet oder innerhalb einer Unterklasse vererbt wird .

Wenn Sie die Variable staticerstellen, wird sie zu einem Mitglied der Klasse, wodurch die Absichten, sie zu erben , beseitigt werden . Dies lässt nur die Absicht, innerhalb eines Pakets verwendet zu werden , und wir haben package-privatedafür (kein Modifikator).

Die einzige Situation , die ich diese nützlich finden könnte, wenn Sie eine Klasse wurden erklärt , die verwendet werden sollen , um die Anwendung zu starten (wie JavaFX ist Application#launch, und wollten nur in der Lage von einer Unterklasse Start sein. Wenn ja dabei darauf, das Verfahren auch finalauf Verstecken nicht zulassen . Dies ist jedoch nicht "die Norm" und wurde wahrscheinlich implementiert, um eine Erhöhung der Komplexität durch Hinzufügen einer neuen Methode zum Starten von Anwendungen zu verhindern.

Informationen zu den Zugriffsebenen der einzelnen Modifikatoren finden Sie in den folgenden Abschnitten: Die Java-Lernprogramme - Steuern des Zugriffs auf Mitglieder einer Klasse


4
Ich verstehe nicht, wie staticdie Absichten, es zu erben, beseitigt würden . Weil meine Unterklasse in einem anderen Paket immer noch das Feld Super benötigt, um protecteddarauf zugreifen zu können, obwohl es das ist static. package-privatekann nicht helfen
Aobo Yang

@AoboYang Du hast recht, weshalb manche Leute es benutzen protected static. Aber es ist ein Code-Geruch, daher der " Run " -Teil. Zugriffsmodifikatoren und Vererbung sind zwei verschiedene Themen. Ja, Sie können von einer Superklasse aus nicht auf ein statisches Mitglied zugreifen, wenn dies der Fall ist package-private. Sie sollten sich jedoch nicht staticin erster Linie auf die Vererbung von Referenzfeldern verlassen . Es ist ein Zeichen für schlechtes Design. Sie werden feststellen, dass Versuche, staticMethoden zu überschreiben , keine Ergebnisse liefern. Dies ist ein klares Zeichen dafür, dass die Vererbung nicht klassenbasiert ist. Wenn Sie Zugang außerhalb der Klasse oder des Pakets benötigen, sollte es seinpublic
Dioxin

3
Ich habe zunächst eine Klasse mit einigen private staticUtil-Funktionen, aber ich denke, jemand möchte meine Klasse verbessern oder anpassen, und diese Util-Funktionen bieten ihnen möglicherweise auch Komfort. publicist möglicherweise nicht geeignet, da util-Methoden nicht für den Benutzer der Instanzen meiner Klasse bestimmt sind. Könnten Sie mir helfen, ein gutes Design zu finden, anstatt protected? Vielen Dank
Aobo Yang

In diesem Link heißt es: "Verwenden Sie die restriktivste Zugriffsebene, die für ein bestimmtes Mitglied sinnvoll ist." Verwenden Sie privat, es sei denn, Sie haben einen guten Grund, dies nicht zu tun. ' , was dieser Frage widerspricht, irgendwelche Gedanken?
Cecilia

13

Ich sehe keinen besonderen Grund, warum dies verpönt werden sollte. Es kann immer Alternativen geben, um dasselbe Verhalten zu erzielen, und es hängt von der tatsächlichen Architektur ab, ob diese Alternativen "besser" sind als eine geschützte statische Methode oder nicht. Ein Beispiel, bei dem eine geschützte statische Methode zumindest sinnvoll wäre, könnte das folgende sein:

(Bearbeitet, um in separate Pakete aufgeteilt zu werden, um die Verwendung protectedklarer zu machen )

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

Daraus abgeleitet:

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

Eine andere abgeleitete Klasse:

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

Der protected staticModifikator kann hier sicherlich gerechtfertigt werden:

  • Die Methoden können sein static, da sie nicht von Instanzvariablen abhängen. Sie sind nicht direkt an, wie eine polymorphe Methode, sondern sind „Nützlichkeit“ Methoden , die bieten Standardbestimmungsgemäßer Verwendung Implementierungen , die Teil einer komplexeren Berechnung sind, und dienen als „Bausteine“ der tatsächlichen Umsetzung.
  • Die Methoden sollten nicht sein public, da sie ein Implementierungsdetail sind. Und sie können nicht sein, privateweil sie von den erweiterten Klassen aufgerufen werden sollten. Sie können auch keine "Standard" -Sichtbarkeit haben, da sie dann für die erweiterten Klassen in anderen Paketen nicht zugänglich sind.

(BEARBEITEN: Man könnte annehmen, dass sich der ursprüngliche Kommentar nur auf Felder und nicht auf Methoden bezog - dann war er jedoch zu allgemein)


In diesem Fall sollten Sie die Standardimplementierungen als protected final(da Sie nicht möchten, dass sie überschrieben werden) und nicht als definieren static. Die Tatsache, dass eine Methode keine Instanzvariablen verwendet, bedeutet nicht, dass dies der Fall sein sollte static(obwohl dies möglich ist ).
Barfuin

2
@ Thomas Sicher, in diesem Fall wäre das auch möglich. Im Allgemeinen: Dies ist sicherlich teilweise subjektiv, aber meine Faustregel lautet: Wenn eine Methode nicht polymorph verwendet werden soll und statisch sein kann, mache ich sie statisch. Im Gegensatz es darum final, nicht nur nicht klar machen , dass das Verfahren nicht außer Kraft gesetzt werden soll, ist es zusätzlich macht den Leser klar , dass das Verfahren nicht Instanzvariablen nicht verwendet. So prägnant: Es gibt einfach keinen Grund, es nicht statisch zu machen.
Marco13

1
Wenn eine Methode nicht polymorph verwendet werden soll und statisch sein kann, mache ich sie statisch. - Dies wird Sie in Schwierigkeiten bringen, wenn Sie anfangen, Schein-Frameworks für Unit-Tests zu verwenden. Aber das führt uns zu einem anderen Thema ...
Barfuin

@ Marco13 gibt es auch den Fall, wenn Sie etwas machen protected, nicht um Vererbung zu zeigen, sondern um es unter einem einzigen Paket zu halten - nicht ausgesetzt. Ich denke, versiegelte Schnittstellen würden auf diese Weise hilfreich sein, wenn sie in Java
Eugene

@ Eugene Dieser Kommentar irritiert mich ein bisschen. Die Sichtbarkeit von protectedPaketen kann ohne Modifikator erreicht werden, während "Klassen erben (auch in verschiedenen Paketen)" bedeutet ...
Marco13

7

Statische Mitglieder werden nicht vererbt, und geschützte Mitglieder sind nur für Unterklassen (und natürlich für die enthaltende Klasse) sichtbar. Daher protected statichat a die gleiche Sichtbarkeit wie static, was auf ein Missverständnis des Codierers hindeutet.


1
Könnten Sie bitte eine Quelle für Ihre erste Aussage angeben? In einem schnellen Test wurde ein geschütztes statisches int problemlos an eine Unterklasse vererbt und wiederverwendet.
Hiergiltdiestfu

Geschützte statische Aufladung hat die gleiche Sichtbarkeit wie paketprivate statische, nicht private statische. Wenn Sie geschützt und statisch verwenden, entfernen Sie am besten einfach den Zugriffsmodifikator, um es paketprivat zu machen (wenn Sie beabsichtigen, es innerhalb des Pakets zugänglich zu machen)
Dioxin

2
Ähm ... nein. Paket privat und protectedsind nicht gleich. Wenn Sie nur sagen static, ist das Feld nur für Unterklassen im selben Paket sichtbar.
Aaron Digulla

1
@AaronDigulla protected staticermöglicht den Zugriff innerhalb des Pakets oder von einer Unterklasse aus . Wenn Sie eine Variable statisch machen, werden die Absichten der Unterklasse entfernt, und die einzige Absicht bleibt der Zugriff aus dem Paket heraus.
Dioxin

@VinceEmigh: Entschuldigung, ich habe mit Bohemian gesprochen. Hätte das klarer machen sollen.
Aaron Digulla

5

Eigentlich ist daran nichts grundsätzlich Falsches protected static. Wenn Sie wirklich eine statische Variable oder Methode wünschen, die für das Paket und alle Unterklassen der deklarierenden Klasse sichtbar ist, machen Sie sie protected static.

Einige Leute meiden es im Allgemeinen protectedaus verschiedenen Gründen, sie zu verwenden, und andere denken, dass nicht endgültige staticVariablen auf jeden Fall vermieden werden sollten (ich persönlich sympathisiere bis zu einem gewissen Grad mit letzteren), also denke ich, dass die Kombination von protectedund für diejenigen, staticdie schlecht aussehen, schlecht aussehen muss gehören zu beiden Gruppen.


3

Geschützt wird verwendet, damit es in Unterklassen verwendet werden kann. Es gibt keine Logik beim Definieren einer geschützten Statik, wenn sie im Kontext konkreter Klassen verwendet wird, da Sie auf dieselbe Variable zugreifen können. Dies ist eine statische Methode. Der Complier gibt jedoch eine Warnung aus, um statisch auf die statische Variable der Superklasse zuzugreifen.


1

Nun, wie die meisten Leute geantwortet haben:

  • protectedbedeutet - ' paketprivat + Sichtbarkeit für Unterklassen - die Eigenschaft / das Verhalten ist VERERBT '
  • staticbedeutet - " das Gegenteil von Instanz - es ist eine CLASS-Eigenschaft / ein CLASS-Verhalten, dh es ist NICHT ERBT "

Daher sind sie leicht widersprüchlich und inkompatibel.

Kürzlich bin ich jedoch auf einen Anwendungsfall gestoßen, bei dem es möglicherweise sinnvoll ist, diese beiden zusammen zu verwenden. Stellen Sie sich vor, Sie möchten eine abstractKlasse erstellen , die für unveränderliche Typen übergeordnet ist und eine Reihe von Eigenschaften aufweist, die den Untertypen gemeinsam sind. Um die Unveränderlichkeit richtig zu implementieren und die Lesbarkeit zu gewährleisten, kann das Builder- Muster verwendet werden.

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

Und warum protected static? Weil ich einen nicht abstrakten Subtyp möchte, AbstactTypeder seinen eigenen nicht abstrakten Builder implementiert und sich außerhalb befindet package X, um auf den zugreifen und ihn wiederverwenden zu können 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;
    }
}

Natürlich können wir die BaseBuilderÖffentlichkeit machen, aber dann kommen wir zu anderen widersprüchlichen Aussagen:

  • Wir haben eine nicht instanziierbare Klasse (abstrakt)
  • Wir stellen einen öffentlichen Erbauer dafür zur Verfügung

Also kombinieren wir in beiden Fällen mit protected staticund publicErbauer einesabstract class widersprüchlichen Aussagen. Es ist eine Frage der persönlichen Vorlieben.

Ich bevorzuge jedoch immer noch den publicErbauer eines,abstract class weil sich das protected staticfür mich in einer OOD- und OOP-Welt unnatürlicher anfühlt!


0

Es ist nichts falsch daran zu haben protected static. Eine Sache, die viele Leute übersehen, ist, dass Sie möglicherweise Testfälle für statische Methoden schreiben möchten, die Sie unter normalen Umständen nicht verfügbar machen möchten. Ich habe festgestellt, dass dies besonders nützlich ist, um Tests für statische Methoden in Dienstprogrammklassen zu schreiben.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.