我正在经历这个问题Is there a way to override class variables in Java? 36条upvotes的第一条评论是:
如果你看到一个
protected static
,跑。
任何人都可以解释为什么protected static
不赞成?
这比直接问题更具风格性。它表明你没有正确地思考课程的内容。
想想static
的意思:
这个变量存在于类级别,它不是为每个实例单独存在,并且它在扩展我的类中没有独立存在。
想想protected
的意思:
这个类可以看到这个变量,同一个包中的类和扩展我的类。
这两个含义并不完全相互排斥,但它非常接近。
我可以看到你可以在哪里使用这两者的唯一情况是,如果你有一个设计为扩展的抽象类,然后扩展类可以使用原始中定义的常量修改行为。尽管如此,这仍然是一个非常微弱的原因,因为你几乎肯定会更好地将常数公之于众。这只会让一切变得更加清洁,并且让人们可以更加灵活地进行分类。
要扩展并解释第一点 - 尝试此示例代码:
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
禁止hiding。但这不是“常态”,并且可能通过添加新方法来启动应用程序来防止增加更多复杂性。
要查看每个修饰符的访问级别,请参阅:The Java Tutorials - Controlling Access to Members of a Class
我没有看到为什么这应该不赞成的特殊原因。可能总是存在实现相同行为的替代方案,并且取决于实际结构,这些替代方案是否比受保护的静态方法“更好”。但是受保护的静态方法至少可以合理的一个例子如下:
(编辑分成单独的包,使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 static
与static
具有相同的可见性,这表明编码器存在误解。
实际上protected static
没有任何根本性的错误。如果你真的想要一个对于包和声明类的所有子类可见的静态变量或方法,那么继续使它成为protected static
。
有些人一般会因各种原因避免使用protected
,有些人认为应该尽量避免使用非最终的static
变量(我个人同情后者在某种程度上),所以我猜protected
和static
的组合必须看起来很糟糕^ 2属于两个群体。
使用Protected以便它可以在子类中使用。在具体类的上下文中使用时,定义受保护的静态没有逻辑,因为您可以以静态方式访问相同的变量。但是,编译器将以静态方式提供访问超类静态变量的警告。
拥有protected static
没有任何问题。许多人忽视的一件事是,您可能希望为静态方法编写测试用例,而这些方法在正常情况下不希望暴露。我注意到这对于在实用程序类中编写静态方法的测试特别有用。
好吧,正如大多数人回答的那样:
protected
的意思是 - 'package-private +子类的可见性 - 属性/行为是INHERITED'static
的意思是 - “与实例相反 - 它是一种CLASS属性/行为,即它不是内在的”因此它们略有矛盾和不相容。
但是,最近我遇到了一个用例,将这两者结合使用可能是有意义的。想象一下,你想要创建一个abstract
类,它是不可变类型的父类,它有一堆子类共有的属性。为了正确实现不变性并保持可读性,可以决定使用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世界中感觉更不自然!