2009-11-14 4 views
19

Я хотел бы создать экземпляр указанного класса, используя его имя. Мой код показан ниже.Generics и Class.forName

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

public static <T> T create(final String className) { 
    try { 
     final Class<?> clazz = Class.forName(className); 

     //WARNING: Type safety: Unchecked cast from capture#2-of ? to T 
     return (T) create(clazz); 
    } 
    catch (ClassNotFoundException e) { 
     e.printStackTrace(); 
    } 
} 

public static <T> T create(final Class<T> classToCreate) { 
    final Constructor<T> constructor; 
    try { 
     constructor = classToCreate.getDeclaredConstructor(); 
     final T result = constructor.newInstance(); 
     return result; 
    } 
    catch (Exception e) { 
     e.printStackTrace(); 
    } 
} 

Благодаря

ответ

28

Я думаю, что первый метод должен выглядеть следующим образом:

public static <T> T create(final String className, Class<T> ifaceClass) 
throws ClassNotFoundException { 
    final Class<T> clazz = Class.forName(className).asSubclass(ifaceClass); 
    return create(clazz); 
} 

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

Кстати, если вы игнорируете эти предупреждения, метод create может создать экземпляр некоторого класса, который несовместим с фактическим типом, используемым вызывающим. Вероятно, это приведет к неожиданному ClassCastException позже; например когда экземпляр назначен.

EDIT: @Pascal указывает, что нам нужно добавить приведение типа, чтобы сделать эту компиляцию; т.е.

Class<T> clazz = (Class<T>) Class.forName(className).asSubclass(ifaceClass); 

К сожалению, мы также нужно добавить аннотацию @SuppressWarnings.

+0

Да, это так близко, вы можете получить с Java. Что касается создания фактического объекта, есть еще несколько трюков, которые нужно сделать, но я не собираюсь вникать в специфику, поскольку эти трюки реализованы в моем классе утилиты. Я планирую выпустить, как только я узнаю канал для распространения среди всех Java-программистов. – Esko

+0

@ Esko, рассмотрите код Google (для выпущенных версий) и/или с Github (для фактического кода) –

+0

Вам нужно добавить бросок, чтобы он скомпилировал 'final Class clazz = (класс ) Class.forName (className). asSubclass (ifaceClass); ' –

2

Я думаю, что это потому, что Class.forName(..) не параметризуется для T. Когда вы запускаете автозаполнение затмения, он принимает clazz.newInstance() return Object. Итак, сохраните бросок и добавьте @SuppressWarnings. Если вы не используете метод должным образом (т. Е. String str = Utils.create("java.util.ArrayList");), тогда произойдет ClassCastException, но это нормально.

+0

create не будет генерировать исключение ClassCastException. Код, вызывающий его, может, но не обязательно сразу. – meriton

+0

Ну, да, но это зависит от того, как он исправляет свои методы, потому что теперь они не компилируются - либо он перерисовывает исключение, либо возвращает null (что вызовет NPE) – Bozho

2

Второй способ в порядке.


Но для первого в качестве параметра может быть передано любое имя класса.

Точка метода должна была бы инициировать класс, который код вызова не знает во время компиляции, он знает об этом только во время выполнения.

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

Поэтому, я бы сказал всего метод не имеет никакого смысла ,


Некоторые точки могут быть уделено методу , если вызывающий код будет знать надтипом класса. Это можно было бы передать как параметр, и приведение было бы возможным и значимым.

public static <T> T create(final Class<T> superType, final String className) { 
    try { 
    final Class<?> clazz = Class.forName(className); 
    final Object object = clazz.newInstance(); 
    if (superType.isInstance(object)) { 
     return (T)object; // safe cast 
    } 
    return null; // or other error 
    } // catch and other error code 
} 
+0

Проверьте метод Class.cast (Object). – meriton

+0

(Чтобы получить более короткий код и устранить это непроверенное предупреждение) – meriton

+0

@Meriton True, если ошибка, выбранная для сообщения о невозможном нажатии, - это выбросить исключение ClassCastException. Например, этот код возвращает null, другой вариант ... Выбор должен быть сделан ... ;-) – KLE

0

Даже если вы можете получить код выше, чтобы работать, метод newInstance() конструктора принимает конструктор без аргументов.

Если класс не имеет одного i.e конструктор нулевого аргумента класса, который вы пытаетесь создать, был явно объявлен приватным или пакетом в зависимости от способа вызова метода, вы получите исключение IllegalAccessException. Что-то добавить к вашему предусловию, если вы его заработаете.

1

Вы не можете ограничить параметр типа, чтобы он содержал тип namedName. Следовательно, вызывающий может предоставить любой тип функции create (String), которая, конечно, не безопасна для типов.

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

public static Object create(final String className) { 
    try { 
     final Class<?> clazz = Class.forName(className); 
     return create(clazz); 
    } 
    catch (ClassNotFoundException e) { 
     e.printStackTrace(); 
    } 
} 

Вызывающий абонент может затем написать:

Foo foo = (Foo) create("my.cool.FooBar"); 

в отличие от

Foo foo = create("my.cool.FooBar", Foo.class); 
0

Я нашел интересную вещь: Type может быть преобразован в Class непосредственно, если это не общий тип , Но я все еще не могу Class.forName("java.util.List<SomeType>").

import java.lang.reflect.*; 
import java.util.*; 

public class Main { 
    public void func(List<List<String>> listlist, Map<String, List<String>> list, String s) {} 

    public static void main(String[] args) throws ClassNotFoundException { 
     Method m = Main.class.getDeclaredMethods()[1]; 

     Type t0 = m.getGenericParameterTypes()[0]; 
     boolean b0 = t0 instanceof Class; 

     boolean b01 = ((ParameterizedType)t0).getRawType() instanceof Class; 

     Type t1 = m.getGenericParameterTypes()[2]; 
     boolean b1 = t1 instanceof Class; 
    } 
} 

enter image description here

Смежные вопросы