Почему мы не должны использовать protected static в java

Я проходил через этот вопрос есть ли способ переопределить переменные класса в Java? Первый комментарий с 36 upvotes был:

Если вы когда-нибудь видели protected static запустите.

может кто-нибудь объяснить, почему это protected static неодобрением?

8 ответов


это скорее стилистическая вещь, чем прямая проблема. Это говорит о том, что вы не продумали должным образом, что происходит с классом.

думать о том, что 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 запретить прячась. Но это не "норма" и, вероятно, было реализовано, чтобы предотвратить добавление большей сложности путем добавления нового способа запуска приложений.

чтобы увидеть уровни доступа о каждом модификаторе см. Следующее:учебники Java-управление доступом к членам класса


Я не вижу особой причины, почему это должно быть неодобрительно. Всегда могут быть альтернативы для достижения одного и того же поведения, и это будет зависеть от фактической архитектуры, являются ли эти альтернативы "лучше", чем защищенный статический метод или нет. Но один пример, когда защищенный статический метод был бы разумным, по крайней мере, может быть следующим:

(отредактировано для разделения на отдельные пакеты, чтобы использовать 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;
    }
}

The protected static модификатор, безусловно, может быть оправдан здесь:

  • методы могут быть static, потому что они не зависят от переменных экземпляра. Они не предназначены для непосредственного использования в качестве полиморфного метода, а скорее являются" служебными " методами, которые предлагают default реализации которые являются частью более сложного вычисления и служат "строительными блоками" фактического реализация.
  • методы не должны быть public, поскольку они являются деталью реализации. И не может быть!--7--> потому что они должны быть вызваны расширения классов. Они также не могут иметь видимость "по умолчанию", потому что тогда они не будут доступны для расширяющих классов в других пакетах.

(EDIT: можно предположить, что исходный комментарий относится только к поля, а не способы - тогда, однако, оно было слишком общим)


статические члены не наследуются, а защищенные члены видны только подклассам (и, конечно, содержащему классу), поэтому protected static имеет ту же видимость, как static, предполагая недоразумение со стороны кодера.


Protected используется, чтобы его можно было использовать в подклассах. Нет логики в определении защищенного статического при использовании в контексте конкретных классов, поскольку вы можете получить доступ к той же переменной статическим способом.Однако complier выдаст предупреждение для доступа к статической переменной super class статическим способом.


на самом деле нет ничего принципиально плохого с protected static. Если вы действительно хотите статическую переменную или метод, который виден для пакета и всех подклассов класса объявления, тогда продолжайте и сделайте это protected static.

некоторые люди обычно избегают использовать protected по разным причинам, и некоторые люди думают, что не окончательный static переменных следует избегать всеми способами (я лично сочувствую последнему в некоторой степени), поэтому я предполагаю комбинацию protected и static надо смотреть плохо^2 для тех, кто принадлежит к обеим группам.


нет ничего плохого в том, что protected static. Одна вещь, многие люди не замечают, что вы можете написать тестовые случаи для статических методов, которые вы не хотите подвергать при нормальных обстоятельствах. Я заметил это особенно полезно для написания тестов для статических методов в классах.


Ну, как большинство людей ответили:

  • protected означает - 'пакет-private + видимость подклассов-свойство / поведение наследуется'
  • static означает - 'противоположность экземпляра-это свойство/поведение класса, i.e не наследуется'

однако недавно я подошел к случаю использования, когда возможно, имеет смысл использовать их вместе. Представьте, что вы хотите создать 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 который реализует свой собственный не абстрактный Строитель и находится вне 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 общественность, но затем мы приходим к другим противоречивым утверждениям:

  • у нас не instantiatable класс (аннотация)
  • мы предоставляем общественный Строитель для него

так в обоих случаях с protected static и public строитель Ан abstract class мы объединяем противоречивые утверждения. Это вопрос личных предпочтений.

тем не менее, я все еще предпочитаю public Строитель в abstract class потому что protected static мне кажется более неестественным в мире УД и ОП !