【问题标题】:Can you rename a variable in a generic Class?您可以重命名泛型类中的变量吗?
【发布时间】:2021-05-24 11:59:36
【问题描述】:

我有一个班级PagedResult。该类可以帮助我实现具有页面格式的不同对象的 JSON 输出。 E 是包含在列表中的对象。一切正常,但有一件事仍然困扰着我。我希望包含对象的列表并不总是具有相同的名称。我想把名字改成对应的对象。

班级PagedResult:

public class PagedResult<E> {

Long totalItems;
Integer totalPages;
Integer currentPage;
List<E> elements;

[... Getter & Setter ...] 
}

带有 MyPojo 这样的对象的实际 JSON 输出如下所示:

{
"totalItems": 2,
"totalPages": 1,
"currentPage": 1,
"elements": [
    {
        "myPojoAttr1": "hello",
        "myPojoAttr2": "there"
    },
    {
        "myPojoAttr1": "hello",
        "myPojoAttr2": "folks"
    }
 ]
}

所以对于每个响应,无论是哪个对象,数组都被命名为 "elements"。我不想在我的 JSON 响应中出现丑陋的名称,因为 PagedResult-class 中的对象不断变化。当我收到带有 MyPojo 之类的对象的响应时,JSON-Array 的名称应该是 "myPojos" 并且当我想获得带有 MyWin 之类的对象的响应时,名称应该是 "我的胜利”

我对@9​​87654330@ 进行了很多尝试,但我找不到方法来实现这个“对象数组名称”也是通用的。有人可以帮我解决这个问题吗?提前致谢。

【问题讨论】:

  • 为什么你想要一个与你类中的内容不对应的名称,如果你想要另一个名称,为什么不更改你类中元素的名称?
  • 因为PagedResult-class 应该能够包装不同的对象。我不想为所有不同的对象创建一个PagedResult-class,例如PagedResultMyPojoPagedResultMyWin 因为重复代码。
  • 在您的 PagedResult 上下文中,无论它们是什么类型,它们都是“元素”
  • 是的。这正是我想通过通用解决方案解决的问题。
  • 改用地图?您不能使用泛型从字符串名称创建类。您需要使用字节码生成库,我认为这在您的情况下是多余的。

标签: java json generics


【解决方案1】:

另一种方法:使用继承。

拥有一个包含所有公共字段的抽象基类PagedResult,然后将其区分为PagedResultWithElementsPagedResultWithMyPojo 等。子类只包含那个“类型”特定的列表。

作为一个缺点:你会得到一些代码重复。但另一方面,您可以更好地控制发生的事情而无需基于自定义代码进行过于复杂的(反)序列化。

因此,当您了解“元素类型”的不同风格时,我们说 3,最多 5 个不同的类,使用继承可能是一个可行的解决方案。

【讨论】:

    【解决方案2】:

    是的,可以使用自定义序列化程序。但即使使用自定义序列化程序,您仍然有一个问题:泛型在编译时被删除。所以我们需要在运行时以某种方式获取类型。

    这里有一个例子,它只检查elements 列表中第一个元素的类型。绝对不是最干净的方法,但您不必调整 PagedResult 类。

    public class PagedResultSerializer<T> extends JsonSerializer<PagedResult<Object>> {
    
        @Override
        public void serialize(PagedResult<Object> value, JsonGenerator gen, SerializerProvider provider) throws IOException {
            gen.writeStartObject();
            gen.writeNumberField("totalItems", value.getTotalItems());
            // Your other attributes
            if (!value.getElements().isEmpty()) {
                Object firstElement = value.getElements().get(0);
                String elementsFieldName;
                if (firstElement instanceof MyPojo) {
                    elementsFieldName = "myPojos";
                } else if (firstElement instanceof MyWin) {
                    elementsFieldName = "myWins";
                } else {
                    throw new IllegalArumentException("Unknown type");
                }
                serializers.defaultSerializeField(elementsFieldName, value.getElements(), gen);
            }
            gen.writeEndObject();
        }
    }
    

    现在你只需要告诉 Jackson 使用这个序列化器而不是默认序列化器。

    @JsonSerialize(using = PagedResultSerializer.class)
    public class PagedResult<T> {
        // Your code
    }
    

    改进:将Class&lt;T&gt; elementsType 属性添加到您的PagedResult 并在您的序列化程序中使用此属性,而不是检查列表中的第一个元素。

    【讨论】:

    • 谢谢你。我认为正确的答案是“不,我不能重命名泛型类中的变量”。但是你的回答帮助我解决了我的问题。
    【解决方案3】:

    没有。你不能那样做。泛型类型具有类型参数,而不是标识符参数。 AFAIK,Java 语言中的任何内容都不允许您在生成类型时将 Java 标识符视为参数。 (当然,在这种情况下你不能使用任何东西!)

    替代方案:

    • 不要这样做。 (仔细研究一下您希望 JSON 属性名称发生变化的原因。它实际上实现了什么?值得付出努力吗?)

    • 不要使用泛型类型。为每种“分页结果”定义不同的类。 (笨重。不推荐。)

    • 使用映射,并为每种“分页结果”的元素属性使用不同的映射键填充它。 (缺点是您会丢失静态类型检查,并且会在性能和存储方面造成很小的损失。但这些影响不大。)

    • 编写自定义映射器以根据您的要求对PagedResult 进行序列化和反序列化。


    不管怎样,标识符作为参数是你可以用宏预处理器做的事情。那种 Java 语言没有对这种东西的标准支持。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-11-05
      • 2016-01-14
      • 1970-01-01
      • 1970-01-01
      • 2010-12-09
      • 1970-01-01
      • 2011-12-27
      • 2013-06-12
      相关资源
      最近更新 更多