web-dev-qa-db-ja.com

javaで保護された静的を使用すべきではない理由

私はこの質問を経験していました Javaでクラス変数をオーバーライドする方法はありますか? 36の賛成票での最初のコメントは:

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

protected staticが眉をひそめている理由は誰でも説明できますか?

111
Zeeshan

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

staticの意味を考えてください:

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

protectedの意味を考えてください:

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

2つの意味は完全に相互に排他的ではありませんが、非常に近いものです。

この2つを一緒に使用できる唯一のケースは、拡張するように設計された抽象クラスがあり、拡張クラスが元で定義された定数を使用して動作を変更できる場合です。それでも、それは非常に微弱な理由ですが、それでもあなたはほぼ確実に定数を公開する方が良いでしょう。これにより、すべてがよりきれいになり、サブクラス化の柔軟性が高まります。

最初のポイントを拡張して説明するには、次のサンプルコードを試してください。

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にアクセスできますが、独自のコピーを継承または取得することはありません。まったく同じ変数を見ています。

76
Tim B

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

変数をprotectedにすることは、それが使用されることを意味しますパッケージ内またはサブクラス内に継承になります。

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

これが役立つ唯一の状況は、アプリケーションの起動に使用するクラス(JavaFXのApplication#launchなど)を宣言し、サブクラスからのみ起動できるようにしたい場合です。メソッドはfinalも許可しないため 非表示 ですが、これは「標準」ではなく、おそらくアプリケーションを起動する新しい方法を追加することで複雑さが増すのを防ぐために実装されました。

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

28
Vince Emigh

私はこれが眉をひそめるべきである特定の理由を見ません。同じ動作を実現する代替手段が常に存在する場合があり、これらの代替手段が保護された静的メソッドよりも「優れている」かどうかは、実際の構造に依存します。しかし、保護された静的メソッドが妥当である少なくとも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にすることはできません。また、他のパッケージの拡張クラスからアクセスできないため、「デフォルト」の可視性を持つこともできません。

(編集:元のコメントはfieldsのみを参照しており、methodsを参照していないと仮定できます-ただし、あまりにも一般的でした)

12
Marco13

静的メンバーは継承されず、保護されたメンバーはサブクラス(およびもちろん含まれるクラス)にのみ表示されるため、protected staticstaticと同じ可視性を持ち、コーダーによる誤解を示唆します。

7
Bohemian

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

一般的に、さまざまな理由でprotectedの使用を避けている人もいれば、最終的なstatic変数を絶対に避けるべきだと思う人もいます(私は個人的にある程度後者に同情しています)。 protectedstaticの両方は、両方のグループに属するものに対してbad ^ 2に見える必要があります。

5
x4u

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

3
Mithun Kannoth

さて、ほとんどの人が答えたように:

  • protectedは次を意味します-'パッケージプライベート+サブクラスの可視性-プロパティ/動作は継承されます'
  • staticは-'インスタンスの反対-CLASSプロパティ/動作です。つまり、継承されていません'

したがって、それらはわずかに矛盾し、互換性がありません。

しかし、最近、これら2つを一緒に使用することが理にかなっているユースケースを思いつきました。 abstractクラスを作成することを想像してください。このクラスはimmutableタイプの親であり、サブタイプに共通する一連のプロパティがあります。 不変性を適切に実装し、可読性を維持するには、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;
    }
}

そして、なぜprotected static?なぜなら、独自の非抽象ビルダーを実装し、AbstactTypeにアクセスして再利用できるようにするために、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をパブリックにすることもできますが、その後、別の矛盾したステートメントになります。

  • インスタンス化不可能なクラス(抽象)があります
  • 私たちはそれに公共のビルダーを提供します

protected staticpublicabstract classビルダーの両方の場合、矛盾するステートメントを組み合わせます。それは個人的な好みの問題です。

しかし、publicビルダーのabstract classの方が好きです-protected staticはOODとOOPの世界では不自然だと感じているからです!

0
egelev

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

0
Aelphaeis