изменение значения нестатической переменной, которая была инициализирована статической переменной, также изменяет статическую переменную - PullRequest
0 голосов
/ 16 марта 2019

Мне нужна помощь!Я пытаюсь получить доступ к значению статической переменной из одного класса в другом классе в проекте Android.Это статический класс ...

public class NamesStore {

  private static NamesStore sNamesStore ;
  private static List<Name> sNames = new ArrayList<>();

  public static NamesStore getInstance() {
    if (sNamesStore == null) {
      return new NamesStore();
    } else {
      return sNamesStore;
    }
  }

  public List<Name> getNames(){
   return sNames;
  }

}

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

public class Utils{

  public static removeTheseNamesFromTheGeneralNames(List<Name> namesToBeRemoved){
      List<Name> names = NamesStore.getInstance().getNames();
      names.removeAll(namesToBeRemoved);
      return names;
  }

}

когда я вызываю метод removeTheseNamesFromTheGeneralNames, статическая переменная в NamesStore также изменяется, и я не могу понять, почему.?Это нормальное поведение статического поля?и если да, пожалуйста, как я могу скопировать значение статического поля и изменить его без изменения значения, хранящегося в статическом поле?Я мог бы сделать это статическим и окончательным, чтобы решить эту проблему, но мне также нужно время от времени устанавливать эту статическую переменную с помощью метода установки.Спасибо за помощь заранее.

Ответы [ 2 ]

1 голос
/ 16 марта 2019

'static' на самом деле не является основной проблемой (хотя это может способствовать).Проблема заключается в методе getNames() и в том, как результат используется вызывающей стороной.

В вашем классе NamesStore есть один список имен (ArrayList) и одна ссылка на этот список имен (sNames).

Когда вы выполняете return sNames и вызывающая сторона присваивает возвращаемое значение его переменной names, у вас все еще остается ровно один список имен (один ArrayList), теперь с двумя ссылками на один и тот же список,Это не следствие «статичности», а следствие того, как Java использует ссылочные типы;короче говоря, присваивание (и возвращение значения) создает копию ссылки, но не создает другого экземпляра объекта, на который ссылаются.

Так, конечно, когда список упоминается как namesмодифицируется, что изменяет единственный существующий список имен.


Если вы хотите, чтобы вызывающая сторона могла изменять список, возвращаемый вашим методом getNames(), без изменения списка, то вынужно вернуть копию вашего списка, а не сам список.

Запись return new ArrayList<>(sNames) сделает это;но обратите внимание, что копируется только список имен, что бы ни было в списке (предположительно, «имена»), это не так.Другими словами, если ваш абонент изменяет фактическое имя, он изменит единственный экземпляр этого имени.Вам необходимо определить ваши требования.

Альтернативный подход - запретить вашему пользователю изменять возвращаемый список;это может быть реализовано как return Collections.unmodifiableList(sNames).Если будет предпринята попытка изменить этот список, код, выполняющий это изменение, потерпит неудачу.Любой, кто хочет изменить список (как в вашем примере), должен сначала сделать себе копию.

Решение между этими двумя, вероятно, принимается на основе рассмотрения того, сколько пользователей getNames() ожидают изменитьрезультат.Если это не обычный случай, то лучше сделать копирование для этих пользователей.

1 голос
/ 16 марта 2019

Я думаю, что лучше сначала прочитать о учениках, чтобы понять, как это работает.https://docs.oracle.com/javase/tutorial/java/javaOO/classvars.html

Мой совет заключается в рефакторинге кода следующим образом:

public class NamesStore {

    private static NamesStore sNamesStore = new NamesStore();
    private List<Name> sNames = new ArrayList<>();

    private NamesStore(){}

    public static NamesStore getInstance() {
        return sNamesStore;
    }

    public List<Name> getNames() {
        return Collections.unmodifiableList(sNames);
    }

    public List<Name> getNamesWithout(List<Name> namesToBeRemoved) {
        return sNames.stream().filter(name -> !namesToBeRemoved.contains(name)).collect(Collectors.toList());
    }

}

ОБЪЯСНЕНИЕ О МЕТОДЕ ФИЛЬТРА

Метод getNamesWithout (СписокnamesToBeRemoved) можно также записать следующим образом:

public List<Name> getNamesWithout(List<Name> namesToBeRemoved) {
    return sNames.stream().filter(new Predicate<Name>() {
        @Override
        public boolean test(Name name) {
            return !namesToBeRemoved.contains(name);
        }
    }).collect(Collectors.toList());
}

Интерфейс Predicate имеет единственный абстрактный метод (единственный метод, который необходимо реализовать в подклассах).В Java интерфейс с одним абстрактным методом называется функциональный интерфейс .Когда вам нужно реализовать функциональный интерфейс, вы можете использовать лямбда-выражение .Что это не значит: вы можете опустить стандартный шаблон и оставить только то, что важно.На рисунке ниже важный код выделен зеленым, а стандартный код красным.

Я надеюсь, что мое объяснение прояснит вам, что с этим именем из лямбда-выражения.

enter image description here

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...