为什么我们不应该在 java 中使用受保护的静态

我正在研究这个问题 < a href = “ https://stackoverflow./questions/685300/Is-there-a-way-to-overage-class-variable-in-Java”> 在 Java 中有没有一种方法可以覆盖类变量? 第一条得到36票赞成的评论是:

如果你看到 protected static,跑。

有人能解释一下为什么 protected static不受欢迎吗?

74959 次浏览

与其说是直接问题,不如说是风格问题。这说明你还没有正确地思考这门课到底是怎么回事。

想想 static是什么意思:

该变量存在于类级别,它不分别存在于每个实例和 它在扩展我的类中没有独立的存在中。

想想 protected是什么意思:

这个变量可以被这个类看到,这些类在同一个包 和延伸我的类中。

这两个意思并不完全相互排斥,但是非常接近。

我能看到的唯一一种情况是,如果您有一个设计为扩展的抽象类,那么扩展类可以使用原始类中定义的常量修改行为,那么您可以将两者结合使用。不过,这种安排最终很可能会非常混乱,表明课程设计的不足。

在大多数情况下,将常量设置为 public 会更好,因为这只会让一切变得更简洁,并允许子类更灵活。在许多情况下,与其他任何东西相比,组合比继承更可取,而抽象类强制继承。

为了看到一个例子来说明这是如何破坏事物的,并说明我所说的没有独立存在的变量是什么意思,请尝试下面的示例代码:

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 staticstatic具有相同的可见性,这表明编码人员存在误解。

我看不出有什么特别的理由要对此表示反对。可能总是存在实现相同行为的替代方法,这将取决于实际的体系结构,这些替代方法是否比受保护的静态方法“更好”。但是,一个受保护的静态方法至少是合理的例子可以是:

(编辑分成单独的包,使 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 是为了能够在子类中使用。当在具体类的上下文中使用时,定义受保护的静态是没有逻辑的,因为可以通过静态方式访问相同的变量。但是,编译器将以静态方式提示访问超类静态变量。

因为自相矛盾,所以不被接受。

制作一个变量 protected意味着它将被使用 在包裹里或它将是 在子类中继承的

使变量 static成为类 消除了继承它的意图的成员。这样就只剩下使用 在一个包裹里的意图了,我们有 package-private(没有修饰符)。

我发现这种方法唯一有用的情况是,如果您声明了一个应该用于启动应用程序的类(比如 JavaFX 的 Application#launch,并且只希望能够从子类启动。如果这样做,确保该方法也是 final以禁止 躲起来。但这并不是“规范”,实施这种方法可能是为了避免增加更多的复杂性,因为它增加了启动应用程序的新方法。

要查看每个修饰符的访问级别,请参见: Java 教程-控制对类成员的访问

实际上,protected static并没有什么根本性的错误。如果您真的想要一个静态变量或方法,这个静态变量或方法对于包和声明类的所有子类都是可见的,那么可以将它设置为 protected static

由于种种原因,一些人通常避免使用 protected,一些人认为非最终的 static变量应该尽量避免(我个人在一定程度上同情后者) ,所以我想 protectedstatic的组合必须看起来 坏 ^ 2属于这两个群体。

拥有 protected static并没有什么错。很多人忽略了一件事,那就是您可能希望为静态方法编写测试用例,而这些方法在正常情况下是不希望公开的。我注意到这对于编写实用程序类中静态方法的测试特别有用。

正如大多数人回答的那样:

  • protected的意思是-‘ Package-private + 子类的可见性-属性/行为是 INHERITED
  • static的意思是-‘ 实例的反义词-它是一个类属性/行为,即它不是继承的

因此,它们之间存在着轻微的矛盾和不相容。

但是,最近我遇到了一个用例,在这个用例中,将这两者结合使用可能是有意义的。假设您想要创建一个 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 static?因为我希望 AbstactType的非抽象子类型能够实现它自己的非抽象 Builder,并且位于 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一个 abstract class的构建器这两种情况下,我们结合了相互矛盾的陈述,这是一个个人偏好的问题。

然而,我仍然喜欢的 一个 abstract class的建造者,因为 protected static对我来说感觉更不自然的面向对象和面向对象的世界!