Статьи

Как получить доступ к значению результата метода из блока finally

В то время как JVM является машиной на основе стека , язык Java на самом деле не предлагает вам никакого способа доступа к этому стеку. Даже если иногда, в редких случаях, это было бы очень полезно.

Пример

Значения результата метода помещаются в стек. Если вы посмотрите на следующий пример:

01
02
03
04
05
06
07
08
09
10
11
public int method() {
    if (something)
        return 1;
 
    ...
    if (somethingElse)
        return 2;
 
    ...
    return 0;
}

Если мы игнорируем проблему остановки , обработку ошибок и другие академические дискуссии, мы можем сказать, что описанный выше метод «безусловно» вернет любое значение 1 , 2 или 0 . И это значение помещается в стек перед выпрыгиванием из метода.

Теперь, иногда, это может быть вариант использования, чтобы предпринять какое-то действие, только когда возвращено данное значение результата. Тогда можно было бы заманить людей к началу старой дискуссии о пламенной войне о том, являются ли множественные операторы return EVIL ™, и весь метод должен был быть сформулирован так:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
public int method() {
    int result = 0;
 
    if (something)
        result = 1;
 
    ...
    if (somethingElse)
        result = 2;
 
    ...
    // Important action here prior to return
    if (result == 1337)
        log.info("hehehe ;-)");
 
    return result;
}

Конечно, приведенный выше пример неверен, потому что ранее if (something) return 1 а if (something) return 2 оператора, немедленно прерывая выполнение метода. Чтобы добиться того же с техникой «одиночный возврат-оператор», нам нужно будет переписать наш код следующим образом:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
public int method() {
    int result = 0;
 
    if (something)
        result = 1;
    else {
 
        ...
        if (somethingElse)
            result = 2;
        else {
            ...
        }
    }
 
    // Important action here prior to return
    if (result == 1337)
        log.info("hehehe ;-)");
 
    return result;
}

… И, конечно же, мы можем продолжать прятаться на велосипедах и использовать пламя, используя фигурные скобки и / или уровни отступов , что показывает, что мы ничего не получили.

Доступ к возвращаемому значению из стека

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

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
public int method() {
    try {
        if (something)
            return 1;
 
        ...
        if (somethingElse)
            return 2;
 
        ...
        return 0;
    }
 
    // Important action here prior to return
    finally {
        if (reflectionMagic.methodResult == 1337)
            log.info("hehehe ;-)");
    }
}

Хорошая новость: да, мы можем! Вот простой трюк, который можно сделать для достижения вышеуказанного:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
public int method() {
    int result = 0;
 
    try {
        if (something)
            return result = 1;
 
        ...
        if (somethingElse)
            return result = 2;
 
        ...
        return result = 0;
    }
 
    // Important action here prior to return
    finally {
        if (result == 1337)
            log.info("hehehe ;-)");
    }
}

Менее хорошие новости: вы никогда не должны забывать явно назначать результат. Но время от времени этот метод может быть очень полезен для «доступа к стеку методов», когда язык Java на самом деле этого не позволяет.

Конечно…

Конечно, вы могли бы просто прибегнуть к этому скучному решению здесь:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
public int method() {
    int result = actualMethod();
 
    if (result == 1337)
        log.info("hehehe ;-)");
 
    return result;
}
 
public int actualMethod() {
    if (something)
        return result = 1;
 
    ...
    if (somethingElse)
        return result = 2;
 
    ...
    return result = 0;
}

… и, вероятно, чаще всего эта техника действительно лучше (потому что она немного более читабельна). Но иногда вы хотите сделать больше, чем просто войти в этот блок finally , или вы хотите получить доступ не только к значению результата, и вы не хотите проводить рефакторинг метода.

Другие подходы?

Теперь твоя очередь. Какой будет ваш предпочтительный альтернативный подход (с примерами кода?) Например, используя монаду Try? Или аспекты?