Перейти к основному содержимому

AbstractMethodError в Java

· 4 мин. чтения

1. Обзор

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

В этом уроке мы более подробно рассмотрим AbstractMethodError . Разберемся, что такое AbstractMethodError и когда это может произойти.

2. Введение в AbstractMethodError

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

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

Мы можем спросить, как мы можем получить эту ошибку во время выполнения?

Во-первых, давайте посмотрим, где AbstractMethodError вписывается в иерархию исключений Java:

java.lang.Object
|_java.lang.Throwable
|_java.lang.Error
|_java.lang.LinkageError
|_java.lang.IncompatibleClassChangeError
|_java.lang.AbstractMethodError

Как видно из приведенной выше иерархии, эта ошибка является подклассом IncompiledClassChangeError . Как следует из названия его родительского класса, AbstractMethodError обычно выдается при наличии несовместимости между скомпилированными классами или JAR-файлами.

Далее давайте разберемся, как эта ошибка может произойти.

3. Как может произойти эта ошибка

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

Допустим, в наше приложение мы включаем библиотеку foreach-queue . Библиотека foreach-queue — это высокоуровневая библиотека спецификаций, которая содержит только один интерфейс:

public interface ForEachQueue {
void enqueue(Object o);
Object dequeue();
}

Кроме того, чтобы использовать интерфейс ForEachQueue , мы импортируем библиотеку реализации ForEachQueue : good-queue . Библиотека good-queue также имеет только один класс:

public class GoodQueue implements ForEachQueue {
@Override
public void enqueue(Object o) {
//implementation
}

@Override
public Object dequeue() {
//implementation
}
}

Теперь, если и good-queue, и foreach-queue находятся в пути к классам, мы можем создать экземпляр ForEachQueue в нашем приложении:

public class Application {
ForEachQueue queue = new GoodQueue();

public void someMethod(Object element) {
queue.enqueue(element);
// ...
queue.dequeue();
// ...
}
}

Все идет нормально.

Когда- то мы узнали, что foreach-queue выпустила версию 2.0 и что она поставляется с новым методом:

public interface ForEachQueue {
void enqueue(Object o);
Object dequeue();

int size();
}

Мы хотим использовать новый метод size() в нашем приложении. Поэтому мы обновляем библиотеку foreach-queue с 1.0 до 2.0 . Однако мы забываем проверить, есть ли новая версия библиотеки good-queue , реализующая изменения интерфейса ForEachQueue .

Поэтому у нас есть good-queue 1.0 и foreach-queue 2.0 в пути к классам.

Далее начинаем использовать новый метод в нашем приложении:

public class Application {
ForEachQueue queue = new GoodQueue();

public void someMethod(Object element) {
// ...
int size = queue.size(); //<-- AbstractMethodError will be thrown
// ...
}
}

Наш код будет скомпилирован без проблем.

Однако, когда строка queue.size() выполняется во время выполнения, будет выброшена ошибка AbstractMethodError . Это связано с тем, что библиотека good-queue 1.0 не реализует метод size() в интерфейсе ForEachQueue .

4. Реальный пример

С помощью простого сценария ForEachQueue и GoodQueue мы можем понять, когда приложение может выдать AbstractMethodError.

В этом разделе мы увидим практический пример AbstractMethodError .

java.sql.Connection — важный интерфейс в JDBC API. Начиная с версии 1.7, в интерфейс Connection было добавлено несколько новых методов , таких как getSchema() .

База данных H2 — это довольно быстрая база данных SQL с открытым исходным кодом. Начиная с версии 1.4.192 добавлена поддержка метода java.sql.Connection.getSchema() . Однако в предыдущих версиях база данных H2 еще не реализовывала этот метод.

Далее мы вызовем метод java.sql.Connection.getSchema() из приложения Java 8 в более старой версии базы данных H2 1.4.191 . Посмотрим, что произойдет.

Давайте создадим класс модульного тестирования, чтобы проверить, вызовет ли вызов метода Connection.getSchema() исключение AbstractMethodError :

class AbstractMethodErrorUnitTest {
private static final String url = "jdbc:h2:mem:A-DATABASE;INIT=CREATE SCHEMA IF NOT EXISTS myschema";
private static final String username = "sa";

@Test
void givenOldH2Database_whenCallgetSchemaMethod_thenThrowAbstractMethodError() throws SQLException {
Connection conn = DriverManager.getConnection(url, username, "");
assertNotNull(conn);
Assertions.assertThrows(AbstractMethodError.class, () -> conn.getSchema());
}
}

Если мы запустим тест, он пройдет, подтверждая, что вызов getSchema() выдает AbstractMethodError .

5. Вывод

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

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

С другой стороны, как только мы столкнемся с AbstractMethodError и хорошо понимаем эту ошибку, мы можем быстро решить проблему.

Как всегда, полный исходный код статьи доступен на GitHub .