Как я могу использовать наследование для типа параметров при вызове метода? - PullRequest
0 голосов
/ 01 декабря 2018

Я занимаюсь разработкой веб-приложения на Java для школьного проекта и следую шаблону архитектуры MVC настолько хорошо, насколько могу;Для этого я создал набор классов JavaBean, а для сериализации и десериализации их экземпляров я использую Gson.Возникшая у меня проблема показывает, что я не до конца понял, как наследование и дженерики работают в Java, поэтому я надеюсь пролить свет на эти аргументы с помощью этого вопроса.

У меня есть два абстрактных класса Item иItemVariant, которые расширяются двумя классами бинов каждый, соответственно CatalogItem, ArchivedItem и CatalogItemVariant, ArchivedItemVariant.Istiance Item может ссылаться на коллекцию ItemVariant, и наоборот, istance 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;
    }
    ...
}

Этовыдача непроверенных кастовых предупреждений, и я знаю, что это, вероятно, не самое элегантное решение;однако, это не главная проблема, с которой я сталкиваюсь.

Классы бинов, которые расширяют эти два, просто добавляют свойства пары каждый вместе со своими получателями и установщиками, и экземпляры этих конкретных классов являются темифактически используется во всем приложении.Теперь возникает реальная проблема: рассмотрим в качестве примера следующую строку JSON, которая должна быть десериализована в экземпляр CatalogItemVariant.

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

Чтобы обойти это, я подумалсделать два метода setVariants в Item и setParentItem в ItemVariant абстрактными, заставив их два подкласса переопределить их.Примерно так:

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 недействительной.

Обходной путь этоотправлять сервлету, который десериализует объект две отдельные строки JSON, одну для варианта элемента и одну для его родительского элемента, десериализовать их оба в два разных объекта, используя правильные классы (CatalogItem для первого и CatalogItemVariantдля второго), а затем вручную установите атрибут parentItem нового экземпляра CatalogItemVariant, используя setParentItem().Конечно, при применении этого решения строка 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;
    }

}

Ошибка, с которой я столкнулся, может быть смоделирована с помощью следующего фрагмента кода, который выдается в последней строке:

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

Дляпояснение, я полностью понимаю, что ошибка вызвана тем, как эти классы разработаны, и я не ожидаю, что программа будет вести себя иначе;Я просто хочу понять, есть ли способ использовать дженерики и наследование для перепроектирования этих классов, и если да, то каков этот путь.

1 Ответ

0 голосов
/ 02 декабря 2018

Для тех, кто сталкивается с этим вопросом, взгляните на это очень простое решение!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...