如果你看到任何一个
protected static
,就逃吧。
有人能解释为什么 protected static
是不受欢迎的吗?
如果你看到任何一个
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
而无需限定名称,但它不会继承或拥有自己的副本。它正在查看内存中完全相同的对象。
Rectangle
)以调整宽度/高度以确保相等性,对于Square
将会产生不希望的结果,如果你用其子类型的实例替换每个超类型。 - Dioxin之所以不被赞同是因为它是自相矛盾的。
将变量设为protected
意味着它将在包内使用或在子类中继承使用。
将变量设为static
将其作为类成员,消除了继承的意图。这只留下了在包内使用的意图,而我们有package-private
来实现这一点(没有修饰符)。
我唯一能找到这个有用的情况是,如果你正在声明一个应该用于启动应用程序的类(就像JavaFX的Application#launch
),并且只希望能够从子类启动。如果这样做,请确保该方法也是final
以防止隐藏。但这不是“常规”,可能是为了防止通过添加新的应用程序启动方式来增加复杂性而实现的。
要查看每个修饰符的访问级别,请参见:Java教程-控制对类成员的访问
protected static
。但这是一种代码异味,因此有了“run”部分。访问修饰符和继承是两个不同的主题。是的,如果它是“包私有的”,你将无法从超类访问静态成员。但你不应该依赖于继承来引用static
字段;这是一个设计不良的迹象。你会注意到尝试覆盖static
方法没有结果,这清楚地表明继承不是基于类的。如果需要在类或包之外进行访问,则应该是public
。 - Dioxinprivate static
的工具函数,但我认为有些人可能希望从我的类中改进或自定义这些工具函数,同时这些工具函数也可以为他们提供便利。使用public
可能不合适,因为工具方法并不是给我的类实例的用户使用的。你能帮我想出一个好的设计来代替protected
吗?谢谢。 - Aobo Yang我并没有看到这件事情被不赞成的特定理由。总会有替代方法来实现相同的行为,而这将取决于实际的架构是否这些替代方法比受保护的静态方法更"好"。但是一个使用受保护的静态方法合理的例子是:
(经过编辑分成单独的软件包,以使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
,因为它们应该由扩展类调用。它们也不能具有“默认”可见性,因为那样它们将无法被其他包中的扩展类访问。(编辑:有人可能认为原始评论只涉及字段,而不是方法 - 但是,那时它太笼统了)
final
不同的是,这不仅清楚地表明该方法不打算被重写,还额外提示读者该方法不使用实例变量。因此,简而言之:没有理由不使其静态。 - Marco13protected
的意思是 “继承类(即使在不同的包中)” … - Marco13@VisibleForTesting
这样的东西... - Eugene静态成员不会被继承,受保护的成员只对子类可见(当然也包括包含类),因此一个 protected static
与 static
具有相同的可见性,这表明程序员存在误解。
protected
不同。如果只使用static
,则该字段仅在同一包中的子类可见。 - Aaron Digullaprotected static
允许在包内或子类中访问。将变量设置为静态会消除子类化的意图,只留下在包内访问的意图。 - Dioxinprotected static
并没有本质上的问题。如果你真的想要一个可在包和所有子类中可见的静态变量或方法,那么就使用 protected static
。protected
,原因各不相同,有些人认为非 final 的 static
变量应该尽可能避免(我个人在某种程度上同意后者),所以我猜对于同时属于这两个群体的人来说,protected
和 static
的组合看起来可能会很“糟糕”。使用protected可以使变量在子类中使用。当在具体类的上下文中访问相同的静态变量时,定义受保护的静态变量是没有逻辑的。然而,在静态方式中访问超类静态变量时,编译器会发出警告。
大多数人已经回答了:
protected
的意思是 - '包私有 + 对子类可见 - 属性/行为被继承'static
的意思是 - '与实例相反 - 它是一个类属性/行为,即它不会被继承'因此,它们略微矛盾和不兼容。
然而,最近我遇到了一种使用情况,其中将这两个结合起来可能是有意义的。想象一下,您想创建一个抽象类,该类是不可变类型的父类,并且具有许多子类型共有的属性。为了正确实现不可变性并保持可读性,可能会决定使用建造者模式。
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;
}
}
AbstactType
子类型,它实现了自己的非抽象构建器并且位于package X
之外,能够访问和重用BaseBuilder
,所以使用了protected static
。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
的抽象类构建器两种情况下,我们都结合了矛盾的声明。这是个人偏好的问题。public
的抽象类构建器,因为在面向对象设计和编程的世界中,对我来说protected static
感觉更加不自然!protected static
没有任何问题。很多人忽略的一点是,你可能想为静态方法编写测试用例,但在正常情况下不想公开此方法。我已经注意到,这对于编写实用类中静态方法的测试非常有用。