2

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

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>>() {
            });

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

此代码使用泽西岛。我认为这并不重要,因为我的“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

4 回答 4

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 ',' !

表明,正如您所期望的元素列表/数组,JSON 表示应该以开始,[但它发现,

就像是

[ {PQR: "abc", abc: "lmn"}, {PQR: "abcd", abc: "lmnd"}]

如果您无法向我们显示 JSON 表示,我建议您尝试在服务器端打印您创建的 JSON...并检查从第一次调用withoutGenerics到第二次“With Generics”的不同之处

于 2013-05-20T17:26:22.140 回答
1

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

这意味着如果你创建一个这样的匿名类new GenericType<List<T>>() { },它就会List<T>脱离这个。不List<Something>; just List<T>,因为这就是类的声明方式。这里T只是被视为一些类型参数。换句话说,为了使子类化技巧GenericType起作用,类型信息必须在编译时进行硬编码。

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

另一种方法是在运行时自己实际构建GenericType对象。该类com.sun.jersey.api.client.GenericType提供了第二个构造函数,它接受一个Type参数,允许您GenericType基于该类型创建一个。Type表示 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; }
        }));
于 2013-05-22T05:27:10.383 回答
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);
}

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

于 2013-05-20T18:36:58.103 回答
0

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

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

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

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

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

于 2013-05-20T17:46:29.933 回答