Мне нужно получить список универсальных ленивых загрузчиков, которые будут создавать экземпляры своего предполагаемого типа

#java #generics #lazy-loading

#java #универсальные #отложенная загрузка

Вопрос:

Извините за загадочное название, но это трудно объяснить. Общее правило заключается в том, что мне нужен ленивый загрузчик, который предоставит мне N экземпляров связанного подстановочного типа. Я называю ленивый загрузчик единицей хранения.

 import java.util.ArrayList;
import java.util.List;

public class StorageUnit<T extends MyInterface> implements Storable<T> {

    private int count;

    public StorageUnit(int count) {
        this.count = count;
    }

    private List<T> storables = new ArrayList<T>();

    public List<T> getInstances(Class<T> c) {
        try {
            if (storables.size() == 0) {
                for (int i = 0; i < count; i  ) {
                    storables.add(c.newInstance());
                }
            } else {
                return storables;
            }
        }catch (IllegalAccessException illegalAccessException) {
            illegalAccessException.printStackTrace();
        } catch (InstantiationException instantiationException) {
            instantiationException.printStackTrace();
        }

        return storables;
    }
}
  

В другом месте моего приложения у меня есть другой класс, который имеет ссылку на несколько из этих единиц хранения. Мне нужно получить экземпляры типа единицы хранения, а затем я что-нибудь сделаю с этим типом.

 import java.util.ArrayList;
import java.util.List;

public class MyStorageUnitContainer {

    private List<StorageUnit<? extends MyInterface>> storageUnits;

    public MyStorageUnitContainer(List<StorageUnit<? extends MyInterface>> storageUnits) {
        this.storageUnits = storageUnits;
    }

    public List<StorageUnit<? extends MyInterface>> getInstances() {
        List<StorageUnit<? extends MyInterface>> instances = new ArrayList<StorageUnit<? extends MyInterface>>();
        for (StorageUnit<? extends MyInterface> storageUnit : storageUnits) {

            storageUnit.getInstances(/* I can't get the bound wildcard... */);
            // Now loop through those instances and do something with them...

        }
        return instances;
    }
}
  

Этот код отстой, поэтому лучшая аналогия, которую я могу придумать, — это реальный контейнер для хранения данных. Этот контейнер для единиц хранения содержит несколько отдельных единиц хранения (например, боксы). Каждый из этих блоков содержит элементы определенного типа (например, бейсбольные карточки). Я знаю, что коробка содержит 100 бейсбольных карточек, но пока я не открою коробку, я ничего не знаю о деталях каждой бейсбольной карточки. По сути, я пытаюсь обрабатывать каждый блок как ленивый загрузчик. Открытие окна загружает N реализаций, если они еще не существуют.

Ответ №1:

Пауло прав, и (также согласно ответу Пауло), я часто просто передаю объект класса в конструктор, чтобы обойти эту проблему. Это позволяет getInstances() методу отображаться так, как вам хотелось бы, то есть без параметров. Внутренне экземпляр сохраняет ссылку на универсальный класс, чтобы он мог вызывать newInstance() его.

Этот код иллюстрирует это на вашем примере. Я протестировал это, и оно выполняется нормально.

 import java.util.ArrayList;
import java.util.List;

public class Sandbox
{
    static interface MyInterface
    {
    }

    static interface Storable<T>
    {
        List<T> getInstances();
    };

    static abstract class MyStorableImpl implements MyInterface
    {
        @Override
        public String toString()
        {
            return "I'm a "   getClass()   " with hashcode "   hashCode();
        }
    }

    static class MyStorable1 extends MyStorableImpl
    {
    }

    static class MyStorable2 extends MyStorableImpl
    {
    }

    static class StorageUnit<T extends MyInterface> implements Storable<T>
    {
        private final int count;

        private final Class<T> clazz;

        public StorageUnit(Class<T> clazz, int count)
        {
            this.count = count;
            this.clazz = clazz;
        }

        private List<T> storables = new ArrayList<T>();

        public List<T> getInstances()
        {
            try
            {
                if (storables.size() == 0)
                {
                    for (int i = 0; i < count; i  )
                    {
                        storables.add(clazz.newInstance());
                    }
                }
                else
                {
                    return storables;
                }
            }
            catch (IllegalAccessException illegalAccessException)
            {
                illegalAccessException.printStackTrace();
            }
            catch (InstantiationException instantiationException)
            {
                instantiationException.printStackTrace();
            }

            return storables;
        }
    }

    static class MyStorageUnitContainer
    {

        private List<StorageUnit<? extends MyInterface>> storageUnits;

        public MyStorageUnitContainer(List<StorageUnit<? extends MyInterface>> storageUnits)
        {
            this.storageUnits = storageUnits;
        }

        public List<MyInterface> getAllInstances()
        {
            List<MyInterface> instances = new ArrayList<MyInterface>();
            for (StorageUnit<? extends MyInterface> storageUnit : storageUnits)
            {
                List<? extends MyInterface> list = storageUnit.getInstances();
                instances.addAll(list);
            }
            return instances;
        }
    }

    public static void main(String[] args)
    {
        StorageUnit<? extends MyInterface> box1 = new StorageUnit<MyStorable1>(MyStorable1.class, 2);
        StorageUnit<? extends MyInterface> box2 = new StorageUnit<MyStorable2>(MyStorable2.class, 3);
        List<StorageUnit<? extends MyInterface>> boxes = new ArrayList<Sandbox.StorageUnit<? extends MyInterface>>();
        boxes.add(box1);
        boxes.add(box2);

        MyStorageUnitContainer container = new MyStorageUnitContainer(boxes);
        List<MyInterface> allInstances = container.getAllInstances();
        for (MyInterface myInterface : allInstances)
        {
            System.out.println(myInterface.toString());
        }
    }

}
  

Комментарии:

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

Ответ №2:

С Java Generics вы не можете получить доступ из переменной типа (или тем более из подстановочного знака) к реальному объекту класса.

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

В вашем случае StorageUnit<T> объекты не содержат никакой информации об используемом здесь T, если вы не предоставите им объект класса правильного типа. Также все они имеют один и тот же объект класса.

Итак, лучшим выбором здесь было бы предоставить объектам StorageUnit объект класса их класса параметров в конструкторе, тогда getInstances() методу не нужно было бы его принимать. Конечно, это только переносит проблему наличия объекта класса в другое место, но где-то это необходимо.