Рубрики
Без рубрики

Проверка того, существует ли класс в Java

Изучите нюансы использования Class.forName() для проверки наличия класса в классе Java

Автор оригинала: Jordan Simpson.

1. Обзор

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

В этом учебнике Мы изумим нюансы использования Class.forName() чтобы проверить наличие класса в классе Java .

2. Использование Class.forName()

Мы можем проверить наличие класса с помощью Java Отражение , в частности, Class.forName() . Документация показывает, что ClassNotFoundException будут брошены, если класс не может быть расположен.

2.1. Когда ожидать ClassNotFoundException

Во-первых, давайте напишем тест, который, безусловно, бросить ClassNotFoundException, чтобы мы могли знать, что наши положительные тесты являются безопасными:

@Test(expected = ClassNotFoundException.class)
public void givenNonExistingClass_whenUsingForName_thenClassNotFound() throws ClassNotFoundException {
    Class.forName("class.that.does.not.exist");
}

Итак, мы доказали, что класс, которого не существует, бросит ClassNotFoundException . Давайте напишем тест для класса, который действительно существует:

@Test
public void givenExistingClass_whenUsingForName_thenNoException() throws ClassNotFoundException {
    Class.forName("java.lang.String");
}

Эти тесты доказывают, что бег Class.forName() и не ловить ClassNotFoundException эквивалентен указанному классу, существующему на classpath . Тем не менее, это не совсем идеальное решение из-за побочные эффекты .

2.2. Побочный эффект: Инициализация класса

Важно отметить, что, без указания погрузчика класса, Class.forName() должен запустить статический инициализатор на запрашиваемом классе . Это может привести к неожиданному поведению.

Чтобы проинкретовать такое поведение, давайте создадим класс, который бросает RuntimeException когда его статический блок инициализатора выполняется, чтобы мы могли сразу знать, когда он выполняется:

public static class InitializingClass {
    static {
        if (true) { //enable throwing of an exception in a static initialization block
            throw new RuntimeException();
        }
    }
}

Мы можем видеть из forName () документации, что он бросает ИсключениеInInitializerError если инициализация, спровоцированная этим методом, не удается.

Давайте напишем тест, который будет ожидать ИсключениеInInitializerError при попытке найти нашу Инициализациякласса без указания погрузчика класса:

@Test(expected = ExceptionInInitializerError.class)
public void givenInitializingClass_whenUsingForName_thenInitializationError() throws ClassNotFoundException {
    Class.forName("path.to.InitializingClass");
}

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

3. Говорить Class.forName() пропустить инициализацию

К счастью для нас, есть перегруженный метод forName (), который принимает загрузчик класса и следует ли выполнять инициализацию класса.

Согласно документации, следующие звонки эквивалентны:

Class.forName("Foo")
Class.forName("Foo", true, this.getClass().getClassLoader())

Изменив истинное ложные , теперь мы можем написать тест, который проверяет на наличие наших Инициализациякласса без запуска его статического блока инициализации :

@Test
public void givenInitializingClass_whenUsingForNameWithoutInitialization_thenNoException() throws ClassNotFoundException {
    Class.forName("path.to.InitializingClass", false, getClass().getClassLoader());
}

4. Модули Java 9

Для проектов Java 9 “, есть третья перегрузка Class.forName() , который принимает Модуль и Струнные название класса. Эта перегрузка не работает инициализатор класса по умолчанию. Кроме того, в частности, он нулевой когда запрошенный класс не существует, а не бросает ClassNotFoundException .

5. Заключение

В этом коротком учебнике мы выявили побочный эффект инициализации класса при использовании Class.forName() и обнаружили, что вы можете использовать forName () перегрузки, чтобы предотвратить это.

Исходный код со всеми примерами в этом учебнике можно найти более на GitHub .