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

时间:2014-06-18 15:09:06

标签: java oop inheritance static protected

我正在审核这个问题Is there a way to override class variables in Java? 36条赞成票的第一条评论是:

  

如果您看到protected static,请运行。

任何人都可以解释为什么protected static不赞成?

8 个答案:

答案 0 :(得分:73)

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

考虑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,而无需限定名称 - 但它不会继承或获取自己的副本。它正在查看完全相同的变量。

答案 1 :(得分:26)

这令人不悦,因为它具有矛盾性。

创建变量protected意味着它将在包中使用,或者它将在子类中继承

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

我能找到的唯一有用的情况是,如果你声明一个应该用于启动应用程序的类(如JavaFX的Application#launch,并且只希望能够从子类启动。)所以,确保该方法也final禁止hiding。但这不是“常态”,并且可能通过添加新方法来启动应用程序来防止增加更多复杂性。

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

答案 2 :(得分:11)

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

(编辑分成单独的包,以使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,因为它们不依赖于实例变量。它们不是直接用作多态方法,而是&#34;实用程序&#34;提供默认实现的方法,这些实现是更复杂计算的一部分,并作为&#34;构建块&#34;实际执行情况。
  • 方法不应该是public,因为它们是实现细节。它们不能private,因为它们应该由扩展类调用。他们也没有&#34;默认&#34;可见性,因为在其他包中扩展类将无法访问它们。

(编辑:可以假设原始评论仅提及字段,而不是方法 - 然而,它太过笼统了)

答案 3 :(得分:7)

静态成员不是继承的,受保护的成员只对子类(当然还有包含的类)可见,因此protected staticstatic具有相同的可见性,这表明编码器存在误解。

答案 4 :(得分:4)

实际上protected static没有任何根本性的错误。如果你真的想要一个对于包和声明类的所有子类可见的静态变量或方法,那么继续使它成为protected static

有些人通常会因各种原因避免使用protected,有些人认为应该尽量避免使用非最终static变量(我个人同情后者),所以我猜测protectedstatic的组合必须将 bad ^ 2 看作属于这两个组的人。

答案 5 :(得分:3)

使用Protected以便可以在子类中使用。在具体类的上下文中使用时,定义受保护的静态没有逻辑,因为您可以以静态方式访问相同的变量。但是,编译器将发出警告以静态方式访问超类静态变量。

答案 6 :(得分:0)

拥有protected static没有错。许多人忽视的一件事是,您可能希望为静态方法编写测试用例,而这些方法在正常情况下不希望暴露。我注意到这对于在实用程序类中编写静态方法的测试特别有用。

答案 7 :(得分:0)

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

  • protected的意思是-'私有包+子类的可见性-属性/行为是继承的'
  • 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公开,但是我们又得出了另一个矛盾的说法:

  • 我们有一个非即时类(抽象)
  • 我们为此提供了一个公共建设者

因此,在使用{strong> protected static public构建器abstract class 的两种情况下,我们都结合了矛盾的陈述。这是个人喜好问题。

但是,我仍然更喜欢public abstract class构建器,因为对于我来说, protected static 对我来说更不自然OOD和OOP世界!