我正在尝试在Retrofit
上创建一个包装器来抽象我的服务实现。到目前为止,我已经成功编译了编译器:
package com.example.spark.testapp.services;
import com.example.spark.testapp.services.apis.Get;
import com.example.spark.testapp.services.apis.Post;
import com.example.spark.testapp.services.utils.*;
import com.example.spark.testapp.services.utils.Error;
import java.util.List;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
import retrofit2.Retrofit;
public class ServiceLayer {
public <T> void performGet(String url, final Class<Get<T>> clazz, com.example.spark.testapp.services.utils.Callback<T> callback) {
Retrofit retrofit = new Retrofit.Builder().baseUrl("").build();
Get<T> service = retrofit.create(clazz);
//Pass authentication token here
Call<T> t = service.get(url, "");
executeCallback(callback,t);
}
public <T> void performPost(String url, final Class<Post<T>> clazz,com.example.spark.testapp.services.utils.Callback<T> callback) {
Retrofit retrofit = new Retrofit.Builder().baseUrl("").build();
Post<T> service = retrofit.create(clazz);
//Pass authentication token here
Call<T> t = service.post(url, "");
executeCallback(callback,t);
}
public <T> void executeCallback( final com.example.spark.testapp.services.utils.Callback<T> callback , Call<T> call) {
call.enqueue(new Callback<T>() {
@Override
public void onResponse(Call<T> call, Response<T> response) {
callback.onSuccess(response.body());
}
@Override
public void onFailure(Call<T> call, Throwable t) {
///Find out what exactly went wrong. Populate Error. and then...
com.example.spark.testapp.services.utils.Error e = new Error();
callback.onFailure(e);
}
});
}
}
在编译时,问题在于调用方法:
private void getString() {
ServiceLayer s = new ServiceLayer();
s.performGet("",Get<String>.class,this); //Cannot select from parameterised type
}
我在Google上搜索了一下,发现由于类型擦除,这是不可能的。精细。
但我的问题是,编译器不应该在这里引发错误吗?在这条线? :
public <T> void performGet(String url, final Class<Get<T>> clazz, com.example.spark.testapp.services.utils.Callback<T> callback)
我的服务层是如何编译的?
修改
这个问题似乎被误解了。我不是在寻找一种方法来使这个设计工作。我理解其中的缺陷,我们找到了更好的方法来分层我们的服务。问题是关于语言本身的有趣/奇怪的行为。
答案 0 :(得分:3)
但我的问题是,编译器不应该在这里引发错误吗?
方法签名在Java中是完全正确的。通用方法签名由与普通方法相同的规则控制。
在通用方法中,您可以在代码中执行的操作取决于参数类型。例如,如果您有这种方法:
public static <T> void test(List<T> list, Class<T> clazz)
throws InstantiationException, IllegalAccessException
{
list.add(clazz.newInstance());
}
它编译,但如果我们添加下一行:
list.add(new Integer(1));
它不会,因为在编译时list
只接受T
的实例。因此,通用方法定义明确。
当您尝试调用泛型方法时,编译器无法从参数中推断出T
。主要问题显然是Class<Get<T>>
构造,它在方法签名中有效但不推荐。虽然,你可以做一个不安全和怪异的强制转换来使方法调用编译和工作:
s.performGet("",(Class<Get<String>>)(Class) Get.class,this);
执行此转换链,编译器现在可以推断T
,因为泛型类型仅在编译时检查。在运行时,Class<Get<T>>
始终为Get.class
。
有关此主题的一些相关问题:
答案 1 :(得分:1)
首先,您的语法有效,因此编译器不会在给定行中显示错误。您遇到的错误可能与Class
是由VM创建的特殊类有关,编译器在检查错误时会将其视为普通类。
看一下这个例子:
class Get<T> {
}
class Callback<T> {
}
class Clazz<T> {
}
static class ServiceLayer {
public <T> void performGet(String url, final Clazz<Get<T>> clazz,
Callback<T> callback) {
}
}
现在如果你试着打电话
ServiceLayer a = new ServiceLayer();
Clazz<Get<String>> clazz = new Clazz<Hello.Get<String>>();
Callback<String> callback = new Callback<String>();
a.performGet("someurl", clazz, callback);
你想要有任何问题。因此,您可以看到该语法没有问题,但有特殊对象。
答案 2 :(得分:0)
做了一些测试并找到了一些解决方案,希望它会有所帮助。
由于此代码返回true(其中A是一些不执行任何操作的泛型类):
A<String> a = new A<String>();
System.out.println(A.class == a.getClass());
这意味着您不需要上课&#39; Get&lt; T&GT;&#39;但只是&#39; Get&#39;为你的改造
你需要的是发送T作为另一个参数,以便知道这个T的功能,例如:
public <T> void performGet(String url, final Class<Get> clazz, final Class<T> t, com.example.spark.testapp.services.utils.Callback<T> callback) {
Retrofit retrofit = new Retrofit.Builder().baseUrl("").build();
Get<T> service = retrofit.create(clazz);
...
答案 3 :(得分:-1)
如果要在方法中保持泛型类参数(T)信息可达,则必须提供类信息。例如:
public class SomeClass<T, X, C> {
Class<X> xClass;
Class<T> tClass;
Class<C> cClass;
public SomeClass<T, X, C>(Class<T> a, Class<X> b, Class<C> c){
tClass = a;
xClass = b;
cClass = c;
}
}
这样,您可以使用“consumer”类中的类信息,用于访问常用签名,序列化/反序列化过程,对象创建,调用泛型方法等。
现在,如果您只想在方法中使用泛型类参数的信息,则应该:
public <T> void performGet(String url, final Class<T> clazz, com.example.spark.testapp.services.utils.Callback<T> callback) {
Retrofit retrofit = new Retrofit.Builder().baseUrl("").build();
Get<T> service = retrofit.create(clazz);
//Pass authentication token here
Call<T> t = service.get(url, "");
executeCallback(callback,t);
}
这允许您将通用类信息保存到包含该方法的类中;您可以在方法中访问它,并将其用作其他通用方法和其他所有方法的参数。
如果您需要更多示例,请查看此内容。它也是一个包装器,但围绕Spring的RestTemplate-Android,所以也许你可以从中得到一些想法。 (免责声明 - 我是开发人员)
https://github.com/fcopardo/EasyRest/blob/master/src/main/java/com/grizzly/rest/GenericRestCall.java