Подтвердить что ты не робот

Загрузка класса из строки

Я хочу создать экземпляр класса по значению String. Я нашел несколько руководств, которые показывают несколько методов для этого. Класс ДОЛЖЕН наследовать от определенного интерфейса, ImplementMe, который имеет специальный метод под названием runMe(). Итак, вот что я пробовал:

ImplmentMe a =
   (ImplementMe) ImplementMe.class
                   .getClassLoader()
                   .loadClass("my.package.IImplementedYou")
                   .newInstance();
a.runMe();

Это работает, но это так уродливо. Я, по крайней мере, ожидал, что вам не понадобится бросок. Скажите, что есть лучший способ.

4b9b3361

Ответ 1

Нет, нет лучшего способа (по дизайну). Вы не должны этого делать, Java разработан как язык с типом. Тем не менее, я могу понять, что вам иногда нужно делать такие вещи, и для этого вы можете создать библиотечную функцию следующим образом:

public <T> T instantiate(final String className, final Class<T> type){
    try{
        return type.cast(Class.forName(className).newInstance());
    } catch(InstantiationException
          | IllegalAccessException
          | ClassNotFoundException e){
        throw new IllegalStateException(e);
    }
}

Теперь ваш код клиента может хотя бы вызвать этот метод без кастования:

MyInterface thingy =
    instantiate("com.foo.bar.MyInterfaceImpl", MyInterface.class);

Ответ 2

Попробуйте Class.forName("my.package.IImplementedYou").

Ответ 3

Вот как бы я это сделал:

ImplementMe noCastNeeded = 
    this.getClassLoader()
        .loadClass("my.package.IImplementedYou")
        .asSubclass(ImplementMe.class).newInstance();

Есть некоторые исключения, чтобы поймать, но это нормально, я думаю.:)

Ответ 4

По сути, это то, что произойдет независимо от того, используете ли вы для него сторонний инструментарий или нет. Литье объекта будет неотъемлемо обязательным, если не ожидается Object. Однако вы можете сделать процедуру, которая сделает это за вас:

public <T> T instantiateObject(String name, Class<T> cls) throws Exception {
    return (T) Class.forName(name).newInstance();
}

Что вы можете использовать:

AClass cls = instantiateObject("com.class.AClass", AClass.class);

Но если вы зашли так далеко, String name на самом деле избыточно (данный класс является классом). Вы также можете:

public <T> T instantiateObject(Class<T> cls) throws Exception {
    return (T) Class.forName(cls.getCanonicalName()).newInstance();
}

Что вы можете использовать:

AClass cls = instantiateObject(AClass.class);

Ответ 5

Вы можете немного сократить его

ImplementMe a = (ImplementMe) Class
                               .forName("my.package.IImplementedYou")
                               .newInstance();

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

Ответ 6

Альтернативой является использование forName, но оно не намного лучше, чем у вас в настоящее время:

ImplementMe a = 
    (ImplementMe) Class.forName("my.package.IImplementedYou").newInstance();
a.runMe();

В самом деле, forName будет использовать getClassLoader().loadClass() за кулисами для загрузки класса, как вы можете видеть в исходный код из Class.java.

Ответ 7

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

Ответ 8

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

Object newInstance = this.getClass().getClassLoader().loadClass("my.package.IImplementedYou").newInstance();

Важно то, что classloader знает как файл класса с реализацией "my.package.IImplementedYou", так и класс с реализацией "ImplementMe".

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

if(newInstance instanceof my.package.IImplementedYou) {  
    ((ImplementMe)newInstance).runMe(); 
}

Вы также можете проверить, что IImlementedYou действительно реализует интерфейс перед созданием экземпляра:

Class c = this.getClass().getClassLoader().loadClass("my.package.IImplementedYou");
if(ImplementMe.class.isAssignableFrom(c)) {
    Object newInstance = c.newInstance(); 
}

Ответ 9

(MyInterface)Class.forName(className).newInstance()