简体   繁体   中英

How can I use inheritance for parameters type in a method call?

I am developing a Java web application for a school project and I am following the MVC architecture pattern as best as I can; to accomplish that, I created a set of JavaBean classes, and to serialize and deserialize instances of these I am using Gson. The problem I'm having shows that I haven't fully understood how inheritance and generics work in Java, so I hope to shed light on those arguments with this question.

I have two abstract classes Item and ItemVariant , which are extended by two bean classes each, respectively CatalogItem , ArchivedItem and CatalogItemVariant , ArchivedItemVariant . An istance of Item can refer to a collection of ItemVariant , and vice versa, an istance of ItemVariant can refer to its parent item (I know this can cause loops in the serialization process but this isn't the problem I am experiencing). So ideally, an instance of CatalogItem should always refer to a collection CatalogItemVariant and an instance of CatalogItemVariant should always refer to one of CatalogItem , and the same applies for ArchivedItem and ArchivedItemVariant . However, I am not interested into forcing this relationship; for example, letting an instance of CatalogItemVariant refer to an instance of ArchivedItem is allowed.

Currently the code for these two abstract classes looks like this:

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

This is producing unchecked cast warnings and I am aware that it probably isn't the most elegant solution; however, this isn't the major issue that I'm experiencing.

The bean classes that extend these two simply add a couple properties each along with their getters and setters, and instances of those concrete classes are the ones actually used throughout the application. Now comes the real problem: consider, as an example, the following JSON string that must be deserialized into an instance of CatalogItemVariant .

{"parentItem":{"name":"Sample name","category":"Sample category","color":"Sample color"},"size":"Sample size"}

Ideally, parentItem should be deserialized into an istance of CatalogItem , but given the way those classes are currently designed, Gson tries to create an instance of Item , which is abstract and thus causes the following exception:

java.lang.RuntimeException: Failed to invoke public model.transfer.catalog.Item() with no args

To work around this, I thought of making the two methods setVariants in Item and setParentItem in ItemVariant abstract, thus forcing their subclasses two override them. Something like this:

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

But this isnt working because the type CatalogItem doesn't match T , making the @Override annotation invalid.

A workaround to this would be sending to the servlet that deserializes the object two separate JSON strings, one for the item variant and one for its parent item, deserialize both of them into two different objects using the correct classes ( CatalogItem for the first one and CatalogItemVariant for the second), and then manually set the attribute parentItem of the new CatalogItemVariant instance using setParentItem() . Of course, when applying this solution, the JSON string for the item variant must miss its parentItem field. Is there a better solution to this? If so, how should I redesign the classes and methods that are affected by this problem?

EDIT : since I've been asked to provide the actual code, here's a simplified version of the classes I'm using:

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

}

The error I'm experienced can be simulated by running the following snippet of code, and it's thrown in the last line:

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

For clarification, I fully understand that the error is caused because of the way these classes are designed and I don't expect the program to behave differently; I just want to understand if there's a way I can use generics and inheritance to redesign those classes, and if so, what that way is.

对于遇到此问题的任何人,请看一下这个非常简单的解决方案!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM