为什么我们不应该在java中使用protected static

问题

我正在经历这个问题Is there a way to override class variables in Java?第一条有36条upvotes的评论是:

如果你看到受保护的静态,请运行。

任何人都可以解释为什么aprotected static被淹没?


#1 热门回答(65 赞)

这比直接问题更具风格性。它表明你没有正确地思考课程的内容。

想想什么是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

classTest2能够访问静态成员testfromTest而无需限定名称 - 但它不会继承或获取自己的副本。它正在查看完全相同的变量。


#2 热门回答(24 赞)

它不赞成因为它是矛盾的。

使变量protected表示它将在packageor中使用,它将被包含在子类中。

使变量static成为该类的成员,消除了继承它的意图.这只留下在包中使用的意图,并且我们有package-private(没有修饰符)。

我发现这个唯一有用的情况是,如果你声明一个应该用于启动应用程序的类(比如JavaFX'sApplication#launch,并且只希望能够从子类启动。如果这样做,请确保方法是但这并不是"常态",可能是为了防止通过添加新的方式来启动应用程序来增加更多复杂性。

要查看每个修饰符的访问级别,请参阅:The Java Tutorials - Controlling Access to Members of a Class


#3 热门回答(11 赞)

我没有看到为什么这应该被不赞成的特殊原因。可能总是存在实现相同行为的替代方案,并且取决于实际结构,这些替代方案是否比受保护的静态方法"更好"。但是,受保护的静态方法至少可以合理的一个例子如下:

(编辑分成单独的包,使用protectedclearer)

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修改器是合理的:

  • 方法可以是静态的,因为它们不依赖于实例变量。它们不是直接用作多态方法,而是"实用"方法,它们提供作为更复杂计算的一部分的默认实现,并充当实际实现的"构建块"。
  • 方法不应该是公开的,因为它们是实现细节。它们不能是私有的,因为它们应该由扩展类调用。它们也不能具有"默认"可见性,因为在其他包中扩展类将无法访问它们。

(编辑:人们可以假设原始评论只提到了tofields,而不是方法 - 然而,它太笼统了)