Подтвердить что ты не робот

Вызов геттера несколько раз или вызов один раз и присвоение переменной?

скажем, предположим, что у меня есть класс:

public class Age {

    private int age;

    public int getAge() {
       return this.age;
    }

}

В моем основном классе я много раз вызываю метод getAge().

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

Что лучше и почему?

4b9b3361

Ответ 1

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

Ответ 2

Не пытайтесь микро-оптимизировать это, если только вы не обнаружите, что это действительно узкое место при профилировании. Я бы использовал метод getAge() accessor, так как это, скорее всего, наиболее удобное и очевидное решение.

Таким образом, оба подхода, вероятно, будут выполняться точно так же. Во время выполнения JIT, скорее всего, полностью оптимизирует вызов getAge(), поэтому в обоих случаях это будет единственный примитивный доступ.

Ответ 3

Может возникнуть некоторая служебная нагрузка при вызове метода getAge() много раз, но я предлагаю вам рассмотреть The Sad Tragedy of the Micro-Optimization Theatre.

Ответ 4

Это то, что вы, как автор API, должны указать вызывающему.

В общем случае, если вы просто возвращаете свойство, вы можете пометить его как окончательный (если вы не предлагаете фактический интерфейс). Это должно снизить затраты на вызовы, поскольку компилятор будет более вероятно встроить эту функцию.

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

Ответ 5

Не беспокойтесь. Это абсолютно не стоит микро-оптимизации так. Подождите, пока вы закончите свой код, затем он будет работать слишком медленно, затем выйдите из профилировщика и поработайте над тем, что говорит профайлер, и это источник проблемы.

Преждевременная оптимизация - это корень всего зла.

Ответ 6

Я попытаюсь сказать примерам кода, что уже сказал другой ответ.

В случае, когда вы представили вызов для getAge(), очень просто, а стоимость его вызова почти ничего. Не беспокойтесь об этом в этом случае.

Но если ваш getAge был чем-то вроде того, что много вычислений или доступа к ресурсам ввода-вывода, например:

public int getAge() {
   return slowService.calculateAgeByBirthDate(birthDate); // it takes 2 seconds to execute for every call
}

Тогда наверняка было бы неплохо кэшировать результат и использовать его. Потому что, если вы назовете это 30 раз, ваш код займет 1 минуту.

Ответ 7

В зависимости от того, как ваше приложение разработано, эти два варианта могут дать разные результаты! Если экземпляр возраста, который вы используете, является общим и изменчивым, разные места в приложении могут изменить его значение между вашими вызовами до getAge(). В этом случае, вопрос правильности решения, который является лучшим вариантом для вашего кода, и это зависит от вас, чтобы решить. Как говорится в старой поговорке: "Сначала сделайте это правильно, а затем сделайте это быстро". И, как уже отмечали другие, вам, вероятно, не придется беспокоиться о части "сделать это быстро" в этом случае.

Связанный пример - это когда вы меняете коллекцию во время итерации через нее. Вы должны перебрать снимок, чтобы не получить ConcurrentModificationException.

Ответ 8

Для простого случая, подобного этому, я бы выбрал тот, который выглядит лучше всего по коду.

Есть несколько случаев, когда желательно позвонить один раз и прочитать сохраненное возвращаемое значение, например, в

for (int i = 0; i < list.size(); i++)
    doSomethingThatDoesNotAffectSizeOfList();

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

В общем случае, если метод вычислительно тяжелый, вы можете использовать memoization, что в основном означает, что вы кешируете уже вычисленный ввод/выходные значения.

Памятная функция "запоминает" результаты, соответствующие некоторому набору конкретных входов. Последующие вызовы с запоминаемыми входами возвращают запоминаемый результат, а не пересчитывают его, тем самым устраняя основную стоимость вызова с заданными параметрами от всех, кроме первого вызова, выполняемого с помощью этих параметров.

Этот метод подталкивает "save-the-return-value-for-efficiency" к самому методу, что облегчает обслуживание bla bla bla...

Ответ 9

Я думаю, вы не увидите разницы во время выполнения - если вы не создаете более одного класса Age.

Ответ 10

Трудная часть - понимание того, что современные JVM делают агрессивные оптимизации при компиляции байтового кода на основе знаний, доступных во время выполнения.

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

Это означает, что get/set для bean attibutes (где значения просто сохраняются и удаляются, а не вычисляются) очень дешевы, и вы должны делать вызовы каждый раз и ожидать, что JVM обнаружит возможные оптимизации и применит их.

Ответ 11

Вы не увидите много изменений в производительности, если только вы не выполняете много операций внутри метода getAge().

Ответ 12

В этом случае я бы предложил не смотреть на производительность, смотреть на удобство использования и повторное использование кода. Ваша текущая реализация - это самый простой из получателей, который возвращает целое число.

Но что, если где-то вниз по линии вы храните дату рождения человека и хотите динамически генерировать возраст? Если вы просто вызываете свойство напрямую, вы вынуждены реорганизовать свой код. Однако, изменяя внутренние элементы getAge(), вы можете поместить там расчет, и все готово.

Мне бы очень хотелось, чтобы языки программирования вводили "супер-частный" модификатор свойства/поля, который в основном говорит "Вы можете получить доступ к этому свойству только через свой аксессор".