Определение типа аргумента в заданном FutureRequest

Скажем, у меня есть этот API, который:

  1. требует много вещей аутентификации.
  2. всегда возвращает JSON, но иногда объект, а иногда массив. Однако легко предсказать, что будет возвращено.

И я хочу использовать эту потрясающую библиотеку Ion ( Koushik Dutta).

Поскольку API, который я использую, требует аутентификации, установки правильных заголовков при каждом запросе и т. Д. Я собираюсь как-то обернуть его, скажем: http://goo.gl/5NLeQn

private void sendRequest(String action, JsonObject params, FutureCallback<JsonObject> callback) {

  String nonce = "" + getNonce();

  Builders.Any.B req = Ion.with(context, BASE_URL + action);

  req.setBodyParameter("key", API_KEY)
    .setBodyParameter("signature", getSignature(nonce))
    .setBodyParameter("nonce", nonce);

  if( params!=null ) {
    for(Map.Entry<String, JsonElement> param : params.entrySet()) {
      JsonElement el = param.getValue();
      if( el.isJsonPrimitive() ) req.setBodyParameter(param.getKey(), el.getAsString());
    }
  }

  req.asJsonObject()
    .setCallback(callback);
}

Это прекрасно работает, пока мне не нужно получить запрос, чтобы вместо того, чтобы быть JsonObject это JsonArray:

java.lang.ClassCastException: com.google.gson.JsonArray нельзя преобразовать в com.google.gson.JsonObject.

В качестве быстрого обходного пути я могу создать два метода, которые перенаправляют поток на один общий, но чтобы упростить ситуацию, скажем, это выглядит так: http://goo.gl/pzSal3.

private void sendRequest(String action, JsonObject params, FutureCallback<JsonObject> callback) {

  String nonce = "" + getNonce();

  Builders.Any.B req = Ion.with(context, BASE_URL + action);

  req.setBodyParameter("key", API_KEY)
    .setBodyParameter("signature", getSignature(nonce))
    .setBodyParameter("nonce", nonce);

  if( params!=null ) {
    for(Map.Entry<String, JsonElement> param : params.entrySet()) {
      JsonElement el = param.getValue();
      if( el.isJsonPrimitive() ) req.setBodyParameter(param.getKey(), el.getAsString());
    }
  }

  req.asJsonObject()
    .setCallback(callback);
}

private void sendRequest(String action, JsonObject params, FutureCallback<JsonArray> callback) {

  String nonce = "" + getNonce();

  Builders.Any.B req = Ion.with(context, BASE_URL + action);

  req.setBodyParameter("key", API_KEY)
    .setBodyParameter("signature", getSignature(nonce))
    .setBodyParameter("nonce", nonce);

  if( params!=null ) {
    for(Map.Entry<String, JsonElement> param : params.entrySet()) {
      JsonElement el = param.getValue();
      if( el.isJsonPrimitive() ) req.setBodyParameter(param.getKey(), el.getAsString());
    }
  }

  req.asJsonArray()
    .setCallback(callback);
}

Но затем BAM, появляется другая ошибка:

'sendRequest (String, JsonObject, FutureCallback)' конфликтует с 'sendRequest(String, JsonObject, FutureCallback)'; оба метода имеют одинаковое стирание

Похоже, что эти типы были урезаны во время выполнения и ВМ запуталась.

Я придумал несколько "решений":

  • Я мог бы использовать FutureRequest вообще не объявляя типы, но тогда они нужны мне в #18-й строке.
  • Я мог бы использовать String вместо этого, а затем проанализировать его с Gson,
  • Я мог бы использовать другой параметр, чтобы указать тип, а затем разыграть callback либо FutureRequest<JsonObject> или же FutureRequest<JsonArray>

Но все они кажутся лишь дешевыми хакерами, заставляющими вещи (как-то) работать. Кто-нибудь здесь знает какое-либо правильное решение этой проблемы?

Мой наиболее предпочтительный способ вызова этих методов:

sendRequest(ACTION_BALANCE, null, new FutureRequest<JsonObject>() { 
    @Override
    public void onCompleted(Exception e, JsonObject result) {
        // my code goes here
    }
});

// OR just

sendRequest(ACTION_OPERATIONS, null, new FutureRequest<JsonArray>() { 
    @Override
    public void onCompleted(Exception e, JsonArray result) {
        // my code goes here
    }
});

2 ответа

Решение

Вместо FutureCallback<JsonObject> а также FutureCallback<JsonArray>почему бы не расширить это так:

public interface JsonObjectFutureCallback extends FutureCallback<JsonObject> { }
public interface JsonArrayFutureCallback extends FutureCallback<JsonArray> { }

Затем вы можете вызвать ваши методы так:

sendRequest(ACTION_BALANCE, null, new JsonObjectFutureCallback() { 
    @Override
    public void onCompleted(Exception e, JsonObject result) {
        // my code goes here
    }
});

// OR just

sendRequest(ACTION_OPERATIONS, null, new JsonArrayFutureCallback() { 
    @Override
    public void onCompleted(Exception e, JsonArray result) {
        // my code goes here
    }
});

Вы можете использовать универсальный тип для передачи обоих FutureCallback<JsonObject> а также FutureCallback<JsonArray> в качестве параметра к тому же методу.

Вам также нужно знать тип T, чтобы вы могли назвать соответствующий req.as(JsonObject/JsonArray)() метод. К сожалению, дженерики Java в основном компилируются, поэтому информация о типах теряется во время выполнения. Вы можете обойти это, передав дополнительный параметр Class в ваш метод.

Поэтому метод sendRequest должен выглядеть так:

private <T>void sendRequest(String action, JsonObject params, FutureCallback<T> callback, Class<?> type) {

    // body of the method

    if(type == JsonObject.class) {
        req.asJsonObject().setCallback(callback);
    }
    else if(type == JsonArray.class) {
        req.asJsonArray().setCallback(callback);
    }
}

И вы можете назвать свой метод так:

sendRequest(ACTION_BALANCE, null, new FutureRequest<JsonObject>() { 
    @Override
    public void onCompleted(Exception e, JsonObject result) {
       // my code goes here
    }
}, JsonObject.class);

// OR just

sendRequest(ACTION_OPERATIONS, null, new FutureRequest<JsonArray>() { 
    @Override
    public void onCompleted(Exception e, JsonArray result) {
        // my code goes here
    }
}, JsonArray.class);
Другие вопросы по тегам