私はこの質問を経験していました Javaでクラス変数をオーバーライドする方法はありますか? 36の賛成票での最初のコメントは:
protected static
が表示されたら、実行してください。
protected static
が眉をひそめている理由は誰でも説明できますか?
それは直接的な問題というよりも文体的なことです。クラスで何が起こっているかを適切に考えていないことを示唆しています。
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
にアクセスできますが、独自のコピーを継承または取得することはありません。まったく同じ変数を見ています。
それは矛盾しているため、眉をひそめています。
変数をprotected
にすることは、それが使用されることを意味しますパッケージ内またはサブクラス内に継承になります。
変数static
を作成すると、変数がクラスのメンバーになり、継承の意図がなくなります。これは、使用されるという意図だけを残しますパッケージ内、そしてそのためのpackage-private
があります(修飾子なし)。
これが役立つ唯一の状況は、アプリケーションの起動に使用するクラス(JavaFXのApplication#launch
など)を宣言し、サブクラスからのみ起動できるようにしたい場合です。メソッドはfinal
も許可しないため 非表示 ですが、これは「標準」ではなく、おそらくアプリケーションを起動する新しい方法を追加することで複雑さが増すのを防ぐために実装されました。
各修飾子のアクセスレベルを確認するには、以下を参照してください。 Javaチュートリアル-クラスのメンバーへのアクセスの制御
私はこれが眉をひそめるべきである特定の理由を見ません。同じ動作を実現する代替手段が常に存在する場合があり、これらの代替手段が保護された静的メソッドよりも「優れている」かどうかは、実際の構造に依存します。しかし、保護された静的メソッドが妥当である少なくとも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を参照していないと仮定できます-ただし、あまりにも一般的でした)
静的メンバーは継承されず、保護されたメンバーはサブクラス(およびもちろん含まれるクラス)にのみ表示されるため、protected static
はstatic
と同じ可視性を持ち、コーダーによる誤解を示唆します。
実際、protected static
に根本的な問題はありません。パッケージと宣言クラスのすべてのサブクラスに表示される静的変数またはメソッドが本当に必要な場合は、先に進み、protected static
にします。
一般的に、さまざまな理由でprotected
の使用を避けている人もいれば、最終的なstatic
変数を絶対に避けるべきだと思う人もいます(私は個人的にある程度後者に同情しています)。 protected
とstatic
の両方は、両方のグループに属するものに対してbad ^ 2に見える必要があります。
保護は、サブクラスで使用できるように使用されます。具象クラスのコンテキストで使用する場合、同じ変数に静的にアクセスできるため、保護された静的を定義するロジックはありませんが、コンパイラーは静的な方法でスーパークラスの静的変数にアクセスするよう警告します。
さて、ほとんどの人が答えたように:
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 static
とpublic
のabstract class
ビルダーの両方の場合、矛盾するステートメントを組み合わせます。それは個人的な好みの問題です。
しかし、public
ビルダーのabstract class
の方が好きです-protected static
はOODとOOPの世界では不自然だと感じているからです!
protected static
を使用しても問題はありません。多くの人が見落としていることの1つは、通常の状況では公開したくない静的メソッドのテストケースを作成することです。これは、ユーティリティクラスの静的メソッドのテストを記述するのに特に役立つことに気付きました。