Доступ к переменным из внутреннего класса

У меня есть код, который определяет анонимный внутренний класс для обработчика обратного вызова. Этот обработчик должен назначить локальную переменную, см. ниже. Мне нужно назначить resp в обратном вызове и обратиться к нему ближе к концу функции. Однако я получаю эту ошибку в Eclipse:

Конечная локальная переменная resp не может быть назначена, так как она определена во включающем типе

Как я могу это исправить?

DoorResult unlockDoor(final LockableDoor door) {
    final UnlockDoorResponse resp;
    final boolean sent = sendRequest(new UnlockDoorRequest(door), 
       new ResponseAction() {
        public void execute(Session session) 
               throws TimedOutException, RetryException, RecoverException {
            session.watch(UNLOCK_DOOR);
            resp = (UnlockDoorResponse)session.watch(UNLOCK_DOOR);
        }
    });
    DoorResult result;
    if (!sent) {
        return DoorResult.COMMS_ERROR;
    }
    else {
        return DoorResult.valueOf(resp.getResponseCode());
    }
}

person fred basset    schedule 25.01.2011    source источник


Ответы (4)


Вот хак, который будет работать в вашем случае:

DoorResult unlockDoor(final LockableDoor door) {
    final UnlockDoorResponse resp[] = { null };
    final boolean sent = sendRequest(new UnlockDoorRequest(door), new ResponseAction() {
        public void execute(Session session)  throws TimedOutException, RetryException, RecoverException {
            session.watch(UNLOCK_DOOR);
            resp[0] = (UnlockDoorResponse)session.watch(UNLOCK_DOOR);
        }
    });
    DoorResult result;
    if (!sent) {
        return DoorResult.COMMS_ERROR;
    }
    else {
        return null == resp[0] ? null : DoorResult.valueOf(resp[0].getResponseCode());
    }
}

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

С уважением, Стэн.

person Stan    schedule 25.01.2011
comment
Интересный хак; почему массив работает, а простая переменная нет? - person fred basset; 25.01.2011
comment
потому что вы не изменяете окончательную ссылку, на которую указывает resp[]. - person Joel; 26.01.2011

Вы можете обойти это, создав класс-оболочку для ответа.

class ResponseWrapper {
    UnlockDoorResponse resp;
    void setResponse(UnlockDoorResponse resp) {
        this.resp = resp;
    }
    UnlockDoorResponse getResponse() {
        return resp;
    }
}

Тогда ваш код будет выглядеть так:

final ResponseWrapper respWrap = new ResponseWrapper();
final boolean sent = sendRequest(new UnlockDoorRequest(door), new ResponseAction() {
    public void execute(Session session)  throws TimedOutException, RetryException, RecoverException {
        session.watch(UNLOCK_DOOR);
        respWrap.setResponse((UnlockDoorResponse)session.watch(UNLOCK_DOOR));
    }
 });
DoorResult result;
if (!sent) {
    return DoorResult.COMMS_ERROR;
}
else {
    return DoorResult.valueOf(respWrap.getResponse().getResponseCode());
}
person jjnguy    schedule 25.01.2011
comment
ResponseWrapper необходимо инициализировать. - person Joel; 25.01.2011

Предполагая, что это ваш код для изменения, как насчет изменения sendRequest и ResponseAction.execute для возврата экземпляра UnlockDoorResponse

DoorResult unlockDoor(final LockableDoor door) {
    final UnlockDoorResponse resp = sendRequest(new UnlockDoorRequest(door), new ResponseAction() {
        public UnlockDoorResponse execute(Session session)  throws TimedOutException, RetryException, RecoverException {
            session.watch(UNLOCK_DOOR);
            return (UnlockDoorResponse)session.watch(UNLOCK_DOOR);
        }
    });
    if (resp == null) {
        return DoorResult.COMMS_ERROR;
    }
    else {
        return DoorResult.valueOf(resp.getResponseCode());
    }
}
person Joel    schedule 25.01.2011

Если вы собираетесь возвращать результаты, используйте именованный внутренний класс вместо анонимного. Все остальные представленные варианты - ИМХО уродливые хаки (один сам признался ;-)

(Хорошо, @Joel's не так, но предполагает, что вы можете изменить интерфейс, который вы реализуете)

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

    class MyReponseAction implements ResponseAction {
        private UnlockDoorResponse response; 

        public void execute(Session session)  throws TimedOutException, RetryException, RecoverException {
           session.watch(UNLOCK_DOOR);
           response = (UnlockDoorResponse)session.watch(UNLOCK_DOOR);
        }

        UnlockDoorResponse getResponse() {
            return response;
        }
    }

   DoorResult unlockDoor(final LockableDoor door) {
        ResponseAction action = new MyResponseAction();
        final boolean sent = sendRequest(new UnlockDoorRequest(door), action);

        DoorResult result;
        if (!sent) {
            return DoorResult.COMMS_ERROR;
        }
        else {
            return DoorResult.valueOf(action.getResponse().getResponseCode());
        }
    }
person Robin    schedule 25.01.2011