如何使用此工作代码并使其使用java泛型?

时间:2013-05-20 17:10:12

标签: java generics jersey

我有一个没有这样定义的泛型的方法:

private void methodWithoutGenerics(Integer subjectKey) {
    Client client = AuthClient.create();
    List<JsonResult> clinicalItems = client
            .resource(getBaseUrl() + "/rest/v1/patients/" + subjectKey + "/" + "results")
            .accept(MediaType.APPLICATION_JSON_TYPE)
            .get(new GenericType<List<JsonResult>>() {
            });

    assertFalse(clinicalItems.isEmpty());

    JsonResult clinicalItem = client
            .resource(getBaseUrl() + "/rest/v1/patients/" + subjectKey + "/" + "results" + "/" + clinicalItems.get(0).getClinicalItemKey())
            .accept(MediaType.APPLICATION_JSON_TYPE)
            .get(new GenericType<JsonResult>() {
            });

    assertNotNull(clinicalItem);
}

此代码正常运行,但我希望能够使用不同的类型调用它:

    methodWithGenerics(subjectKey, "results", JsonResult.class);
    methodWithGenerics(subjectKey, "medications", JsonMedication.class);
    methodWithGenerics(subjectKey, "allergies", JsonAllergy.class);

而且我无法找到一种方法来生成上述方法,以便它与此签名“一起工作”。我担心我必须复制并粘贴输入的东西三次。

右侧的所有这些类都有一个名为JsonClinicalItem的基类。它有一个我在名为getClinicalItemKey()的临床项目上调用的方法。

我怎样才能弄清楚这一点?它甚至可能吗?


以下是我的尝试:

private <T extends JsonClinicalItem> void methodWithGenerics(Integer subjectKey, String pluralName, Class<T> t) {
    Client client = AuthClient.create();
    List<T> clinicalItems = client
            .resource(getBaseUrl() + "/rest/v1/patients/" + subjectKey + "/" + pluralName)
            .accept(MediaType.APPLICATION_JSON_TYPE)
            .get(new GenericType<List<T>>() {
            });

    assertFalse(clinicalItems.isEmpty());

    T clinicalItem = client
            .resource(getBaseUrl() + "/rest/v1/patients/" + subjectKey + "/" + pluralName + "/" + clinicalItems.get(0).getClinicalItemKey())
            .accept(MediaType.APPLICATION_JSON_TYPE)
            .get(new GenericType<T>() {
            });

    assertNotNull(clinicalItem);
}

这会编译,但是当我运行我的代码时,methodWithGenerics(subjectKey, "results", JsonResult.class);会抛出一个类强制转换异常。很明显,我编写这种泛型方法的方式与原始方法的方式有所不同。话虽这么说,我比较了两次调用之间的服务器输出,它们是相同的。这是堆栈跟踪:

java.lang.ClassCastException: java.lang.reflect.Method cannot be cast to java.lang.Class
    at com.owlike.genson.reflect.TypeUtil.getTypes(TypeUtil.java:362)
    at com.owlike.genson.reflect.TypeUtil.match(TypeUtil.java:298)
    at com.owlike.genson.convert.BasicConvertersFactory.provide(BasicConvertersFactory.java:102)
    at com.owlike.genson.convert.BasicConvertersFactory.create(BasicConvertersFactory.java:74)
    at com.owlike.genson.convert.BasicConvertersFactory.create(BasicConvertersFactory.java:56)
    at com.owlike.genson.convert.ChainedFactory.create(ChainedFactory.java:93)
    at com.owlike.genson.convert.ChainedFactory.create(ChainedFactory.java:80)
    at com.owlike.genson.convert.ChainedFactory.create(ChainedFactory.java:93)
    at com.owlike.genson.convert.ChainedFactory.create(ChainedFactory.java:80)
    at com.owlike.genson.convert.ChainedFactory.create(ChainedFactory.java:93)
    at com.owlike.genson.convert.ChainedFactory.create(ChainedFactory.java:80)
    at com.owlike.genson.convert.CircularClassReferenceConverterFactory.create(CircularClassReferenceConverterFactory.java:58)
    at com.owlike.genson.convert.CircularClassReferenceConverterFactory.create(CircularClassReferenceConverterFactory.java:22)
    at com.owlike.genson.Genson.provideConverter(Genson.java:182)
    at com.owlike.genson.convert.DefaultConverters$CollectionConverterFactory.create(DefaultConverters.java:115)
    at com.owlike.genson.convert.DefaultConverters$CollectionConverterFactory.create(DefaultConverters.java:106)
    at com.owlike.genson.convert.BasicConvertersFactory.provide(BasicConvertersFactory.java:102)
    at com.owlike.genson.convert.BasicConvertersFactory.create(BasicConvertersFactory.java:74)
    at com.owlike.genson.convert.BasicConvertersFactory.create(BasicConvertersFactory.java:56)
    at com.owlike.genson.convert.ChainedFactory.create(ChainedFactory.java:93)
    at com.owlike.genson.convert.ChainedFactory.create(ChainedFactory.java:80)
    at com.owlike.genson.convert.ChainedFactory.create(ChainedFactory.java:93)
    at com.owlike.genson.convert.ChainedFactory.create(ChainedFactory.java:80)
    at com.owlike.genson.convert.ChainedFactory.create(ChainedFactory.java:93)
    at com.owlike.genson.convert.ChainedFactory.create(ChainedFactory.java:80)
    at com.owlike.genson.convert.CircularClassReferenceConverterFactory.create(CircularClassReferenceConverterFactory.java:58)
    at com.owlike.genson.convert.CircularClassReferenceConverterFactory.create(CircularClassReferenceConverterFactory.java:22)
    at com.owlike.genson.Genson.provideConverter(Genson.java:182)
    at com.owlike.genson.Genson.deserialize(Genson.java:330)
    at com.owlike.genson.ext.jersey.GensonJsonConverter.readFrom(GensonJsonConverter.java:124)
    at com.sun.jersey.api.client.ClientResponse.getEntity(ClientResponse.java:565)
    at com.sun.jersey.api.client.ClientResponse.getEntity(ClientResponse.java:535)
    at com.sun.jersey.api.client.WebResource.handle(WebResource.java:696)
    at com.sun.jersey.api.client.WebResource.access$300(WebResource.java:74)
    at com.sun.jersey.api.client.WebResource$Builder.get(WebResource.java:512)
    at com.mirth.results.rest.resource.UrlsWorkIntegrationTest.methodWithGenerics(UrlsWorkIntegrationTest.java:114)
    at com.mirth.results.rest.resource.UrlsWorkIntegrationTest.canTraverseUrls(UrlsWorkIntegrationTest.java:85)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:45)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:42)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:20)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:263)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:68)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:47)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:231)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:60)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:229)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:50)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:222)
    at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:28)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:300)
    at org.junit.runner.JUnitCore.run(JUnitCore.java:157)
    at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:77)
    at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:195)
    at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:63)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:120)

这句话的错误是:

    List<T> clinicalItems = client
            .resource(getBaseUrl() + "/rest/v1/patients/" + subjectKey + "/" + pluralName)
            .accept(MediaType.APPLICATION_JSON_TYPE)
            .get(new GenericType<List<T>>() {
            });

如果我运行methodWithoutGenerics,则不会发生此错误。因此,我不理解的两者之间的泛型必定存在一些差异。

此代码正在使用Jersey。我认为这无关紧要,因为我的“withoutGenerics”方法工作正常,所以这可能不是泽西岛问题。但这是我对泽西岛的依赖:

        <dependency>
            <groupId>com.sun.jersey</groupId>
            <artifactId>jersey-server</artifactId>
            <version>1.17</version>
        </dependency>
        <dependency>
            <groupId>com.sun.jersey</groupId>
            <artifactId>jersey-servlet</artifactId>
            <version>1.17</version>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>com.sun.jersey</groupId>
            <artifactId>jersey-server-linking</artifactId>
            <version>1.17.1</version>
        </dependency>
        <dependency>
            <groupId>com.sun.jersey</groupId>
            <artifactId>jersey-client</artifactId>
            <version>1.17.1</version>
        </dependency>

4 个答案:

答案 0 :(得分:1)

你需要为类参数改变这样的方法

private <T extends JsonClinicalItem> void methodWithGenerics(
    Integer subjectKey, String pluralName, Class<T> t) {

这种变化肯定是必要的......

Illegal character at row 0 and column 763 expected [ but read ',' !

表明,正如您期望的List / Element元素一样,JSON表示应该以{{1​​}}开头,但它会找到[

类似

,

如果您无法向我们显示JSON表示,我建议您尝试在服务器端打印您创建的JSON ...并检查从第一次调用[ {PQR: "abc", abc: "lmn"}, {PQR: "abcd", abc: "lmnd"}] 到第二次“使用泛型”的不同之处/ p>

答案 1 :(得分:1)

GenericType通过查看自己的类声明,从自己的类声明中获取泛型超类型的参数来工作。当你创建一个像这个new GenericType<List<JsonResult>>() { }这样的匿名类时,该类的超类型是GenericType<List<JsonResult>>,这个声明信息存储在字节码中,可以在运行时检索,它可以得到List<JsonResult>它的。

这意味着如果你创建一个像这个new GenericType<List<T>>() { }这样的匿名类,它就会从字面上List<T>。不是List<Something>;只是List<T>,因为这就是声明类的方式。这里T被视为某种类型参数。换句话说,对于GenericType的子类化技巧,类型信息必须在编译时进行硬编码。

那么如果您的函数必须为多种类型中的一种执行此操作会发生什么?正如您所发现的,一种解决方案是让调用者传入适当构造的GenericType对象。调用者可能会通过使用具有硬编码类型的子类来获得它(可能每个调用位置只需要为一种类型执行此操作,因此它可以对其进行硬编码)。

另一种方法是在运行时自己构建GenericType对象。 com.sun.jersey.api.client.GenericType类提供了第二个构造函数,它接受Type参数,允许您基于该类型创建GenericTypeType表示Java中的任何泛型或非泛型类型。对于非泛型类型,只需使用类对象:

T clinicalItem = client
        .resource(getBaseUrl() + "/rest/v1/patients/" + subjectKey + "/" + pluralName + "/" + clinicalItems.get(0).getClinicalItemKey())
        .accept(MediaType.APPLICATION_JSON_TYPE)
        .get(new GenericType<T>(t));

对于泛型类型,您必须创建实现ParameterizedType接口的东西。您可以实现自己的类来执行此操作,或者在某处执行现有实现,但我将只显示简化版本:

List<T> clinicalItems = client
        .resource(getBaseUrl() + "/rest/v1/patients/" + subjectKey + "/" + pluralName)
        .accept(MediaType.APPLICATION_JSON_TYPE)
        .get(new GenericType<List<T>>(new ParameterizedType() {
            public Type[] getActualTypeArguments() { return new Type[]{t}; }
            public Type getRawType() { return List.class; }
            public Type getOwnerType() { return null; }
        }));

答案 2 :(得分:0)

对我来说,看起来get()方法在构造自身时使用GenericType的类型参数信息,这在运行时会被删除。

我之前没有和泽西合作过。但基于API文档的一个问题和一个建议:

  • 您如何使用默认构造函数实例化GenericType?它似乎受到javadoc here保护。

  • 您应该尝试使用提供的带有“Type”参数的备用构造函数。

标准java.lang.Class实现了java.lang.reflect.Type接口。所以我想你只需要将Class参数作为Type实例传递。

答案 3 :(得分:0)

为了解决我的问题,我稍微更改了方法的签名,所以现在我这样称呼它:

    methodWithGenerics(subjectKey, "results", new GenericType<List<JsonResult>>() {}, new GenericType<JsonResult>() {});
    methodWithGenerics(subjectKey, "medications", new GenericType<List<JsonMedication>>() {}, new GenericType<JsonMedication>() {});
    methodWithGenerics(subjectKey, "allergies", new GenericType<List<JsonClinicalItem>>() {}, new GenericType<JsonClinicalItem>() {});

现在我的方法看起来像这样:

private <T extends JsonClinicalItem> void methodWithGenerics(Integer subjectKey, String plural, GenericType<List<T>> listType, GenericType<T> resultType) {
    Client client = AuthClient.create();
    List<T> clinicalItems = client
            .resource(getBaseUrl() + "/rest/v1/patients/" + subjectKey + "/" + plural)
            .accept(MediaType.APPLICATION_JSON_TYPE)
            .get(listType);

    assertFalse(clinicalItems.isEmpty());

    T clinicalItem = client
            .resource(getBaseUrl() + "/rest/v1/patients/" + subjectKey + "/" + plural + "/" + clinicalItems.get(0).getClinicalItemKey())
            .accept(MediaType.APPLICATION_JSON_TYPE)
            .get(resultType);

    assertNotNull(clinicalItem);
}

我不确定为什么与人们帮助我的所有建议相比,这有什么不同,但是编译并运行没有错误。其他建议都没有对我有用,即使它们是好主意。