【发布时间】:2018-11-30 23:00:19
【问题描述】:
我正在为一个学校项目开发一个 Java Web 应用程序,并且我尽可能地遵循 MVC 架构模式;为此,我创建了一组 JavaBean 类,并使用 Gson 对这些实例进行序列化和反序列化。我遇到的问题表明我还没有完全理解继承和泛型在 Java 中是如何工作的,所以我希望通过这个问题阐明这些论点。
我有两个抽象类Item和ItemVariant,它们分别由两个bean类扩展,分别为CatalogItem、ArchivedItem和CatalogItemVariant、ArchivedItemVariant。
Item 的实例可以引用ItemVariant 的集合,反之亦然,ItemVariant 的实例可以引用其父项(我知道这会导致序列化过程中出现循环,但这不是问题我正在经历)。所以理想情况下,CatalogItem 的实例应该始终引用集合CatalogItemVariant,CatalogItemVariant 的实例应该始终引用CatalogItem 之一,ArchivedItem 和ArchivedItemVariant 也是如此。但是,我对强迫这种关系不感兴趣。例如,允许CatalogItemVariant 的实例引用ArchivedItem 的实例。
目前这两个抽象类的代码如下:
public abstract class Item implements Serializable {
...
protected Collection<ItemVariant> variants;
...
public <T extends ItemVariant> Collection<T> getVariants() {
return (Collection<T>) variants;
}
...
public <T extends ItemVariant> void setVariants(Collection<T> variants) {
this.variants = (Collection<ItemVariant>) variants;
}
}
public abstract class ItemVariant implements Serializable {
...
protected Item parentItem;
...
public <T extends Item> T getParentItem() {
return (T) parentItem;
}
...
public <T extends Item> void setParentItem(T parentItem) {
this.parentItem = parentItem;
}
...
}
这会产生未经检查的强制转换警告,我知道这可能不是最优雅的解决方案;但是,这不是我遇到的主要问题。
扩展这两个的 bean 类只需添加几个属性以及它们的 getter 和 setter,这些具体类的实例是整个应用程序中实际使用的实例。现在真正的问题来了:例如,考虑以下必须反序列化为 CatalogItemVariant 实例的 JSON 字符串。
{"parentItem":{"name":"Sample name","category":"Sample category","color":"Sample color"},"size":"Sample size"}
理想情况下,parentItem 应该被反序列化为 CatalogItem 的实例,但考虑到这些类当前的设计方式,Gson 会尝试创建 Item 的实例,它是抽象的,因此会导致以下异常:
java.lang.RuntimeException: Failed to invoke public model.transfer.catalog.Item() with no args
为了解决这个问题,我想将Item 中的setVariants 和ItemVariant 中的setParentItem 两个方法抽象化,从而强制它们的子类两个覆盖它们。像这样的:
public abstract class ItemVariant implements Serializable {
...
protected Item parentItem;
...
public abstract <T extends Item> void setParentItem(T parentItem);
...
}
public class CatalogItemVariant extends ItemVariant {
...
@Override
public void setParentItem(CatalogItem parentItem) {
this.parentItem = parentItem;
}
...
}
但这不起作用,因为CatalogItem 类型与T 不匹配,导致@Override 注释无效。
解决方法是向 servlet 发送反序列化对象的两个单独的 JSON 字符串,一个用于项目变体,一个用于其父项,使用正确的类将它们反序列化为两个不同的对象 (CatalogItem第一个和CatalogItemVariant 第二个),然后使用setParentItem() 手动设置新CatalogItemVariant 实例的属性parentItem。当然,在应用此解决方案时,项目变体的 JSON 字符串必须错过其 parentItem 字段。有没有更好的解决方案?如果是这样,我应该如何重新设计受此问题影响的类和方法?
编辑:因为我被要求提供实际代码,所以这里是我正在使用的类的简化版本:
public abstract class Item implements Serializable {
private static final long serialVersionUID = -6170402744115745097L;
protected String name;
protected String category;
protected String color;
protected Collection<ItemVariant> variants;
public String getName() {
return this.name;
}
public String getCategory() {
return this.category;
}
public String getColor() {
return this.color;
}
public <T extends ItemVariant> Collection<T> getVariants() {
return (Collection<T>) variants;
}
public void setName(String name) {
this.name = name;
}
public void setCategory(String category) {
this.category = category;
}
public void setColor(String color) {
this.color = color;
}
public <T extends ItemVariant> void setVariants(Collection<T> variants) {
this.variants = (Collection<ItemVariant>) variants;
}
}
public abstract class ItemVariant implements Serializable {
private static final long serialVersionUID = 4245549003952140725L;
protected Item parentItem;
protected String size;
public <T extends Item> T getParentItem() {
return (T) parentItem;
}
public String getSize() {
return size;
}
public <T extends Item> void setParentItem(T parentItem) {
this.parentItem = parentItem;
}
public void setSize(String size) {
this.size = size;
}
}
public class CatalogItem extends Item implements Serializable {
private static final long serialVersionUID = 993286101083002293L;
protected String description;
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
}
public class CatalogItemVariant extends ItemVariant implements Serializable {
private static final long serialVersionUID = -2266390484210707778L;
protected Integer availability;
public Integer getAvailability() {
return availability;
}
public void setAvailability(Integer availability) {
this.availability = availability;
}
}
我遇到的错误可以通过运行以下sn-p代码来模拟,并在最后一行抛出:
Gson gson = new GsonBuilder().create();
CatalogItem catalogItem;
CatalogItemVariant catalogItemVariant;
CatalogItemVariant deserializedCatalogItemVariant;
catalogItem = new CatalogItem();
catalogItem.setName("Sample name");
catalogItem.setCategory("Sample category");
catalogItem.setColor("Sample color");
catalogItemVariant = new CatalogItemVariant();
catalogItemVariant.setSize("Sample size");
catalogItemVariant.setParentItem(catalogItem);
String serializedCatalogItemVariant = gson.toJson(catalogItemVariant); // Builds a JSON string of the object to serialize
System.out.println(serializedCatalogItemVariant); // Prints the JSON string of the serialized object which is fed for deserialization in the next instruction
deserializedCatalogItemVariant = gson.fromJson(serializedCatalogItemVariant, CatalogItemVariant.class);
为了澄清,我完全理解错误是由于这些类的设计方式引起的,我不希望程序的行为有所不同;我只是想了解是否有一种方法可以使用泛型和继承来重新设计这些类,如果是,那是什么方法。
【问题讨论】:
-
“我正在为一个学校项目开发一个 Java Web 应用程序” - Alex,很好地提出了一个带有描述的问题以供我们继续,您已经清楚地了解了您的问题并且尝试了正确的事情。我们可以看看具体的类和序列化/反序列化的代码行吗?这样我们就可以将其放入调试器中并快速为您提供答案。
-
@RobertBain 刚刚编辑了原帖
标签: java inheritance javabeans json-deserialization abstraction