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


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

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

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

8 106

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

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

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

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

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

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

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

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

  • protected означает - 'package-private + visibility to subclasses-свойство / поведение наследуется'
  • static означает - 'противоположность instance - это свойство/поведение класса, т. е. оно не наследуется'

поэтому они немного противоречивы и несовместимы.

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

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

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

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