Вызовите блок метода только для первого вызова метода

У меня есть метод и внутри этого метода у меня есть блок:

public void method()
{
   [block instructions]
}

Но этот метод дважды вызывается в моей программе. Я хотел бы, чтобы этот блок выполнялся только один раз и только для первого появления метода. Какой был бы лучший и элегантный способ сделать это?

Ответ 1

private static final AtomicBoolean hasRunAtom = new AtomicBoolean();

public void method() {
  if (hasRunAtom.getAndSet(true)) return;
  [block instructions]
}

Ответ 2

Из-за риска чрезмерной инженерии я на самом деле предлагаю state-pattern. По существу у вас есть абстракция State:

interface State extends Runnable {}

с двумя реализациями:

class FirstState extends State {
    public void run() {
        //[block of code]
        state = new SecondState();
    }
}

class SecondState extends State {
    public void run() {
        //[block instructions]
    }
}

FirstState переключает ток State:

private State state = new FirstState();

В вашем method() теперь нет условной логики:

public void method()
{
    state.run();
}

Однако в 99% случаев флаг boolean достаточно...

UPDATE: вышеприведенное решение не является потокобезопасным. Если вам это нужно, просто AtomicReference<State> state не будет будет достаточно (см. Комментарий Марко Топольника ниже) или вам нужно синхронизировать все method():

public synchronized void method()
{
    state.run();
}

Ответ 3

Простым решением было бы использовать статический логический флаг:

static boolean flag = true;

public void method()
{  
  if (flag)
  {

      [block instructions]
      flag = false;  

  }
} 

Ответ 4

Прежде всего, если вы хотите легко и быстро, используйте простой булевский флаг (просто не забудьте проблему concurrency и используйте решение @Marko Topolnik).

Но если мы уже открыли теоретическое обсуждение и говорили о шаблоне проектирования, вместо того, чтобы использовать государственный шаблон, например, упоминание @Tomasz Nurkiewicz, я бы предложил подумать о singleton (некоторые говорят, что это более анти-шаблон дизайна) и как достичь только одного экземпляра этого класса, поэтому если запрашивать экземпляр этого класса, конструктор будет вызываться только при первом вызове.

если вы alentivly хотите больше "вне коробки", вы можете использовать

Ответ 5

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

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

method() {
    // Do stuff that run only the first time
    method2()
}

method2() {
    // Do stuff that run both times
}

Если он вызвал из ТОЛЬКО места, вы должны спросить себя, почему ваш код ожидает, что во втором случае произойдет что-то другое.

Ответ 6

Ответ Марко прост концептуально, но каждый вызов метода теперь должен выполнять атомную операцию на AtomicBoolean. Если метод() очень часто называется, это приведет к значительному снижению производительности.

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

public class MyClass {
    public void method() {
        BlockRunner.runIfFirst();
    }

    private static class BlockRunner {
        static {
            [block insturctions]
        }

        public static void runIfFirst() {}
    }
}

Это решение основано на концепции Singleton lazy-init описанной здесь.

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