我有一个没有泛型定义的方法,如下所示:

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的基类。它具有一种称为“ cc”的临床项目的方法。

我该如何概括呢?可能吗



这是我尝试过的:

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


这样可以编译,但是当我运行代码时,getClinicalItemKey()会引发类强制转换异常。因此,很明显,我编写此通用方法的方式与原始方法的外观有所不同。话虽如此,我已经比较了两个调用之间的服务器输出,它们是相同的。这是堆栈跟踪:

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


如果运行methodWithGenerics(subjectKey, "results", JsonResult.class);,则不会发生此错误。因此,两者之间的泛型必须存在一些我不了解的差异。

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

        <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>

最佳答案

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

08-04 18:38