Javaで保護されたstaticを使用してはならない理由


121

私はこの質問を通過しましたJavaでクラス変数をオーバーライドする方法はありますか? 36の賛成票を持つ最初のコメントは次のとおりです。

が表示された場合はprotected static、実行してください。

誰がなぜprotected static眉をひそめているのか説明できますか?


6
である限り、保護された静的フィールドに問題はありませんfinal。クラス間で共有される変更可能な静的フィールドは、間違いなく心配の種です。静的フィールドを更新する複数のクラスは、信頼性が高くない、または追跡が容易ではない可能性があります。特に、保護されたフィールドまたはメソッドの存在は、クラスが他のパッケージのクラスによって拡張されることを意味しているため、おそらく保護されたフィールドを含むクラスの作成者。
VGR 2014年

6
@VGR finalは、フィールドが不変であることを意味しません。参照変数objectによって参照されるものはいつでも変更できfinalます。
Zeeshan 2014年

@VGR同意しない。静的変数を作成する唯一の理由は、継承によってのみ別のパッケージ内からアクセスできるようにすることであり、単一のフィールドへのアクセスが継承の理由であってはなりません。これは欠陥のある設計、IMOであり、それに頼るなら、おそらくアプリケーションの構造を再考する必要があります。それは私の意見ですが。
ダイオキシン2014年

@LoneRiderそうですね。私は不変だと思っていましたが、最終的にはそれが保証されるわけではありません。
VGR 2014年

私も同じ質問から来ました。
Raj Rajeshwar Singh Rathore

回答:


86

それは直接的な問題というよりは文体的なものです。それはあなたがクラスで何が起こっているのかを適切に考えていなかったことを示唆しています。

static意味を考えてみてください。

この変数はクラスレベルに存在し、インスタンスごとに個別に存在することはありません。また、私を拡張するクラスに独立して存在することもありません

protected意味を考えてみてください。

この変数は、このクラス、同じパッケージ内のクラス、および私を拡張するクラスから見ることができます。

2つの意味は、相互に排他的ではありませんが、かなり近いです。

2つを一緒に使用できる唯一のケースは、拡張するように設計された抽象クラスがあり、拡張クラスが元のクラスで定義された定数を使用して動作を変更できる場合です。このような配置は、おそらく非常に厄介なものになり、クラスの設計の弱点を示しています。

ほとんどの場合、定数をpublicにすると、すべてがよりクリーンになり、サブクラス化の柔軟性が向上するため、より適切です。抽象クラスは継承を強制しますが、多くの場合、他のものとはかなり離れて構成が継承よりも望ましいです。

これがどのように物事を壊すことができるかの1つの例を見て、独立した存在を持たない変数によって私が何を意味するかを説明するには、次の例のコードを試してください。

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

結果が表示されます。

test
changed

https://ideone.com/KM8u8Oで試してみてください

このクラスは、Test2静的メンバにアクセスすることが可能であるtestから、Test名前を修飾する必要なし-しかし、それは継承しないか、独自のコピーを取得します。メモリ内のまったく同じオブジェクトを調べています。


4
君たちは相続に夢中になっている。これが見られる典型的なケースは、公開せずにパッケージへのアクセスを望んでいる人です(例:単体テスト)。
spudone 2014年

3
@spudoneですが、単体テストは通常​​同じパッケージに入れられます。それらにアクセス権を与えるには、デフォルト(パッケージ)アクセスレベルを使用します。Protectedは、サブクラスへのアクセスも提供します。これは、単体テストには必要ないか、関連していません。
Tim B

1
@Kamafeather Protectedは、子供が異なるクラスからあなたを見ることができ、同じパッケージ内のクラスがあなたを見ることができることを意味します。つまり、プログラムは同じパッケージ内にあるため、保護されたメンバーを見ることができます。
Tim B

2
拡張するクラスは動作を変更する可能性があります」これはLiskov Subsitutionの原則に違反します。サブクラスは、そのスーパータイプの動作を変更するべきではありません。これは、「正方形は長方形ではない」という引数全体に該当します。すべてのスーパータイプをそのサブタイプのインスタンスで置き換える場合、スーパークラス(Rectangle)を調整して幅/高さを調整して()が等しくなるようにするSquareと、望ましくない結果が生じます。
ダイオキシン

1
2つを一緒に使用できる唯一のケースは、拡張するように設計された抽象クラスがあり、拡張クラスが元のクラスで定義された定数を使用して動作を変更できる場合です。少し隠されていますが、そこ。コード例は次のステートメントも表現しています
ダイオキシン

32

それは矛盾しているのでそれは眉をひそめています。

変数を作成するprotected、パッケージ内で使用されるか、サブクラス内継承されることになります。

変数staticを作成すると、変数がクラスのメンバーになり、継承する意図がなくなります。これは、パッケージ内で使用する意図のみを残し、私たちはそれpackage-privateをします(修飾子はありません)。

あなたはJavaFXののようなアプリケーションを(起動するために使用されなければならないクラスを宣言した場合、私はこの便利を見つけることができる唯一の状況がありApplication#launch、そして唯一のサブクラスから起動できるようにしたかった。そうする場合は、この方法でもあることを確認finalします不許可隠れる。しかし、これは「当たり前」ではない、おそらく打ち上げアプリケーションへの新しい方法を追加することによって、より複雑にすることを防止するために実装されました。

各修飾子のアクセスレベルを確認するには、次を参照してください。Javaチュートリアル-クラスのメンバーへのアクセスの制御


4
staticそれを継承する意図をどのように排除するのか私にはわかりません。別のパッケージの私のサブクラスはprotected、それがアクセスするために、スーパーのフィールドにアクセスする必要があるためですstaticpackage-private助けられない
Aobo Yang

@AoboYangその通りです。そのため、一部の人々はを使用していますprotected static。しかし、それはコードのにおいなので、「実行」の部分です。アクセス修飾子と継承は2つの異なる主題です。はい、そうであれば、スーパークラスから静的メンバーにアクセスすることはできませんpackage-private。ただしstatic、そもそも継承に依存してフィールドを参照するべきではありません。それは貧弱なデザインのしるしです。staticメソッドをオーバーライドしようとしても結果が得られないことがわかります。これは、継承がクラスベースではないことを明確に示しています。クラスまたはパッケージの外からアクセスする必要がある場合は、次のようにする必要がありますpublic
ダイオキシン

3
private static最初はいくつかのutil関数を含むクラスがありますが、誰かが私のクラスから改善またはカスタマイズしたいと思うかもしれません。これらのutil関数もそれらに便利を提供するかもしれません。publicutilメソッドは私のクラスのインスタンスのユーザー向けではないため、適切ではない可能性があります。代わりに良いデザインを理解するのを手伝ってくれませんprotectedか?ありがとう
Aobo Yang

そのリンクでは、「特定のメンバーにとって意味のある最も制限されたアクセスレベルを使用します。正当な理由がない限り、プライベートを使用してください。」、この質問、どんな考えと矛盾していますか?
セシリア、

13

これが眉をひそめられるべきである特定の理由を私は見ません。同じ動作を実現する代替手段は常に存在する可能性があり、これらの代替手段が保護された静的メソッドよりも「優れている」かどうかは、実際のアーキテクチャに依存します。しかし、保護された静的メソッドが妥当である1つの例は、少なくとも次のとおりです。

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

それから派生:

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

別の派生クラス:

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

protected static修飾子は確かにここに正当化することができます。

  • メソッドはstaticインスタンス変数に依存しないため、にすることができます。これらは、ポリモーフィックメソッドとして直接使用するためのものではなく、より複雑な計算の一部であるデフォルトの実装を提供し、実際の実装の「ビルディングブロック」として機能する「ユーティリティ」メソッドです。
  • メソッドはpublic実装の詳細であるため、にしないでください。そして、それらはprivate、拡張クラスによって呼び出されるべきなので、そうすることはできません。また、他のパッケージの拡張クラスからアクセスできないため、「デフォルト」の可視性を持つこともできません。

(編集:元のコメントはメソッドではなくフィールドのみを参照していたと想定できますが、それでは一般的すぎます)


この場合、デフォルトの実装をprotected final(それらをオーバーライドしたくないため)ではなく、として定義する必要がありますstatic。メソッドがインスタンス変数を使用しないという事実は、それがそうである必要があることを意味しませんstatic(それは可能ですが)。
バルフイン2014年

2
@Thomasもちろん、この場合、これも可能です。一般に、これは確かに部分的に主観的ですが、私の経験則は次のとおりです。メソッドが多態的に使用されることを意図しておらず、静的である可能性がある場合は、静的にします。これを作成するのとfinalは対照的に、メソッドがオーバーライドされることを意図していないことが明確になるだけでなく、メソッドがインスタンス変数を使用しないことも読者に明確になります。だから簡潔に言うと、それを静的にしない理由はありません
Marco13 2014年

1
メソッドが多態的に使用されることを意図しておらず、静的である可能性がある場合は、静的にします。-これは、単体テストでモックフレームワークの使用を開始するときに問題が発生します。しかし、これは私たちに別のトピックを導きます...
barfuin '18 / 06/18

@ Marco13 protected継承を示すためではなく、単一のパッケージの下に保持するために何かを作成する場合もあります-公開されません。密封されたインターフェースは、Javaでこのようになるとこのように役立つと思います
Eugene

@Eugeneそのコメントは私を少しいらいらさせます。パッケージの可視性は修飾子なしで達成できますが、protected「クラスを継承する(異なるパッケージでも)」...を
意味し

7

静的メンバーは継承されず、保護されたメンバーはサブクラス(もちろん、それを含むクラス)にのみ表示されるため、protected staticはと同じようstaticに表示され、コーダーによる誤解を示唆しています。


1
最初の声明の出典を教えてください。簡単なテストでは、保護された静的整数がサブクラスに継承され、問題なく再利用されました。
hiergiltdiestfu 14年

保護されたstaticは、private staticではなく、package-private staticと同じ可視性を持っています。これに追加して、保護された静的なものを使用している場合は、アクセス修飾子を削除してパッケージを非公開にすることをお勧めします(パッケージ内でアクセスできるようにする場合)
Dioxin

2
うーん...いや。パッケージはプライベートでありprotected、同じではありません。とだけ言うstaticと、フィールドは同じパッケージ内のサブクラスにのみ表示されます。
アーロン・ディグラ2014年

1
@AaronDigullaをprotected static使用すると、パッケージ内またはサブクラスからアクセスできます。変数を静的にすると、サブクラス化の意図がなくなり、パッケージ内からのアクセスのみが意図されます。
ダイオキシン2014年

@VinceEmigh:すみません、私はボヘミアンと話していました。これをもっと明確にすべきだった。
アーロン・ディグラ2014年

5

実際、には根本的な問題はありませんprotected static。パッケージと宣言クラスのすべてのサブクラスに表示される静的変数またはメソッドが本当に必要な場合は、先に進んでそれを作成してくださいprotected static

一部の人々は、一般的に使用を避けるprotected非最終考える様々な理由や何人かの人々のためにstatic、私はの組み合わせを推測するので、変数は、(私は個人的にある程度後者に同情)是非とも避けるべきであるprotectedstatic見なければならない2 ^悪いことをしたものに両方のグループに属しています。


3

Protectedを使用して、サブクラスで使用できるようにします。具象クラスのコンテキストで使用する場合、同じ変数にアクセスできるので静的な方法で保護されたstaticを定義するロジックはありませんが、コンパイラーは静的な方法でスーパークラスの静的変数にアクセスするよう警告します。


1

まあ、ほとんどの人が答えたように:

  • protectedつまり、「package-private +サブクラスへの可視性-プロパティ/動作は継承されます
  • static意味-「インスタンスの反対-クラスのプロパティ/動作です。つまり、継承されません

したがって、それらは少し矛盾しており、互換性がありません。

しかし、最近、私はこれら2つを一緒に使用することが理にかなっているかもしれないユースケースを思いつきました。不変の型のabstract親であり、サブタイプに共通の一連のプロパティを持つクラスを作成するとします。不変性を適切に実装し、読みやすさを維持するために、ビルダーパターンを使用することができます。

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

そしてなぜprotected staticAbstactType独自の非抽象ビルダーを実装し、外部package Xに配置され、にアクセスして再利用できる非抽象サブタイプが必要だからです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;
    }
}

もちろん、BaseBuilder公開することはできますが、それから別の矛盾する声明に行きます。

  • インスタンス化できないクラスがあります(抽象)
  • パブリックビルダーを提供しています

したがって、with protected staticとのpublicビルダーのabstract class両方で、矛盾するステートメントを組み合わせます。それは個人的な好みの問題です。

ただし、OODとOOPの世界では不自然に感じるので、私はまだのpublicビルダーをabstract class好みprotected staticます!


0

持っていることに何の問題もありませんprotected static。多くの人が見落としていることの1つは、通常の状況では公開したくない静的メソッドのテストケースを記述したい場合があることです。これは、ユーティリティクラスで静的メソッドのテストを作成する場合に特に便利です。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.