Обобщения Java с объектом класса как универсальным типом

Я хочу создать объект Java для класса, который определяется с помощью универсальных типов. В частности, я хочу создать список объектов класса, который определяется во время выполнения.

Я бы хотел что-то вроде

Class clazz = Class.forName("MyClass");
List<clazz> myList = new ArrayList<>(); // This isn't allowed

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

Есть ли способ добиться чего-то вроде приведенного выше кода с помощью java.

«но это привело бы к преобразованию объектов каждый раз, когда объект выбирается из списка, я бы хотел избежать такого сценария». Ничто из того, что вы можете сделать, не избавит вас от необходимости каждый раз приводить объекты, кроме небезопасного приведения к списку, что не является необоснованным.

Louis Wasserman 10.08.2018 23:24

@LouisWasserman, но как нам привести к List <clazz>, поскольку эта структура не разрешена java, преобразование отдельных объектов с помощью Reflections простое, но можем ли мы преобразовать общий класс, используя тот же синтаксис?

Aman J 10.08.2018 23:59

Это невозможно, поскольку универсальные типы разрешаются во время компиляции, поэтому во время выполнения универсальные типы в основном не существуют. Как вы собираетесь использовать список после его объявления? В любом случае, в зависимости от того, как вы это решите, кастинг не должен вызывать беспокойства.

Alberto Hormazabal 11.08.2018 00:09

Вы этого не сделаете. Пишите List без дженериков. Если у вас когда-либо был Class<T>, вы можете транслировать его на List<T>.

Louis Wasserman 11.08.2018 00:16

@LouisWasserman Я бы предпочел List<?> необработанному списку. На одно предупреждение меньше.

VGR 12.08.2018 17:39

@VGR, к сожалению, это сделает список только для чтения

Eugene 14.08.2018 11:38

@Amal J: Я действительно этого не понимаю: вы говорите, что вам нужно «приводить объекты каждый раз, когда объект выбирается из списка». Но если тип clazz неизвестен до времени выполнения, как узнать, что преобразовать к? К какому типу вы относитесь?

Lii 24.08.2018 08:59

@Eugene Предложенный вами подход в моем случае был невозможен. В любом случае спасибо за предложение.

Aman J 12.09.2018 13:33
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
1
8
1 472
4

Ответы 4

Что ж, поскольку вы знать этого класса, вы можете (с предупреждением) преобразовать сам List; но вам все равно нужно знать имя класса и некоторые проверки для этого, например:

if (clazz.getName().equals("java.lang.String")) {
     // warning here
     yourList = (List<String>) yourList; 
}

Имя класса определяется во время выполнения, и это может быть множество классов, и написать такую ​​логику для решения будет невозможно.

Aman J 10.08.2018 23:57

@AmanJ, могут ли ваши классы расширять общий интерфейс, чтобы вы могли преобразовать его в List<ThatInterface>? В противном случае приведение каждого элемента будет единственным решением.

Eugene 11.08.2018 12:49

Нет, не можешь. Обобщения в Java - это просто механизм проверки типов во время компиляции. Если вы не знаете тип до времени выполнения, то, очевидно, его нельзя использовать для проверки типа во время компиляции. Компилятор не может определить во время компиляции, какие типы разрешить вам вставлять или выходить из List<clazz>, поэтому это не более значимо, чем просто необработанный тип List.

Насколько я понимаю ваш вопрос, вы хотите знать, может ли компилятор узнать тип среды выполнения, когда он ограничен типом компиляции.

Вы не можете. И ваша гипотеза тоже неверна:

Defining an array of object would allow me to store a list of MyClass type objects, but that would lead to casting the objects every-time the object is fetched from the list, i would like to avoid such a scenario.

В Java дженерики не удаляют приведение: оно все еще присутствует в форме стирания типа и (скрытого) приведения. Когда вы выполняете List<String>, вы просто просите компилятор скрыть приведение в действии, например T get(int): будет приведение к String.

Если вы хотите использовать информацию о времени компиляции, это будет означать, что у вас уже есть / известен тип MyClass, доступный во время компиляции, и вы не будете использовать Class::forName, а MyClass.class, который вернет Class<MyClass>.

Что вы можете сделать, так это:

  1. Используйте интерфейс, если у вас есть точки соприкосновения с этими классами (например, JDBC Driver).
  2. Приведите необработанный список к известному типу, например, используя Class::isAssignableFrom.

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

public <T> void worksWithSomeUnknownClass() throws ReflectiveOperationException {
    @SuppressWarnings("unchecked")
    Class<T> clazz = (Class<T>) Class.forName("MyClass");

    T obj = clazz.getConstructor().newInstance();

    List<T> myList = new ArrayList<>();
    myList.add(obj);
}

Однако это решение очень ограничено. Это гарантирует, что вы не перепутаете его с другими неизвестными типами или Object, но вы не можете ничего делать с T. И вы должны объявить параметр типа для каждого метода, который его использует.

Другие вопросы по теме