Помилка множинного наслідування
Java — це широко використовувана, об'єктно-орієнтована мова програмування, яка значною мірою покладається на наслідування для сприяння повторному використанню коду та модульності. Однак багато розробників стикаються з певною проблемою при спробі наслідувати кілька класів у Java: "Синтаксична помилка на токенах, очікується RecordHeaderName замість цього" або подібна помилка. У цій статті пояснюється, чому це трапляється, і наводяться практичні рішення.
Проблема
Припустімо, що ви працюєте над фреймворком для автоматизації тестування на основі Selenium. Ви вирішуєте організувати свій код, створивши багаторазові допоміжні класи. Ось приклад:
Клас SeleniumHelper:
public class SeleniumHelper {
protected WebDriver driver;
public SeleniumHelper(WebDriver driver) {
this.driver = driver;
}
}
Клас LetsShopHeaderSection:
public class LetsShopHeaderSection extends SeleniumHelper {
public LetsShopHeaderSection(WebDriver driver) {
super(driver);
PageFactory.initElements(driver, this);
}
}
Тепер ви створюєте ще один клас об'єкта сторінки, LetsShopListingPage
, який має успадкувати функціональність від обох класів SeleniumHelper
і LetsShopHeaderSection
:
Клас LetsShopListingPage:
public class LetsShopListingPage extends SeleniumHelper, LetsShopHeaderSection {
public LetsShopListingPage(WebDriver driver) {
super(driver);
PageFactory.initElements(driver, this);
}
}
Помилка
Коли ви намагаєтесь скомпілювати цей код, ви стикаєтеся з такою помилкою:
Синтаксична помилка на токенах, очікується RecordHeaderName замість цього Java(1610612973)
Чому це трапляється?
Java не підтримує множинне наслідування для класів. Клас може розширювати лише один батьківський клас. Це дизайнерське рішення уникає складностей, таких як Проблема діаманта, коли виникають неясності через наслідування одного методу з кількох батьківських класів.
У наведеному вище прикладі клас LetsShopListingPage
намагається успадкувати від обох класів SeleniumHelper
і LetsShopHeaderSection
, що порушує правило одиничного наслідування в Java.
Рішення
1. Використовувати ланцюг наслідування
Оскільки LetsShopHeaderSection
вже розширює SeleniumHelper
, клас LetsShopListingPage
може опосередковано успадкувати від SeleniumHelper
, розширюючи лише LetsShopHeaderSection
.
Ось оновлений код:
public class LetsShopListingPage extends LetsShopHeaderSection {
public LetsShopListingPage(WebDriver driver) {
super(driver);
PageFactory.initElements(driver, this);
}
}
Чому це працює: У цьому підході клас LetsShopListingPage
успадковує від LetsShopHeaderSection
, який, у свою чергу, успадковує від SeleniumHelper
. Це утворює ланцюг наслідування:
LetsShopHeaderSection
→SeleniumHelper
LetsShopListingPage
→LetsShopHeaderSection
→SeleniumHelper
2. Використовувати композицію замість наслідування
Якщо вам потрібна функціональність як від SeleniumHelper
, так і від LetsShopHeaderSection
, але ви не хочете покладатися на наслідування, ви можете використовувати композицію. У цьому підході клас LetsShopListingPage
містить екземпляри обох класів і делегує виклики їх методам.
public class LetsShopListingPage {
private SeleniumHelper seleniumHelper;
private LetsShopHeaderSection letsShopHeaderSection;
public LetsShopListingPage(WebDriver driver) {
this.seleniumHelper = new SeleniumHelper(driver);
this.letsShopHeaderSection = new LetsShopHeaderSection(driver);
PageFactory.initElements(driver, this);
}
}
Чому це працює: Композиція зовсім не використовує наслідування і сприяє гнучкості, дозволяючи вам отримати доступ до методів як з SeleniumHelper
, так і з LetsShopHeaderSection
без створення жорсткої ієрархії.
3. Використовувати інтерфейси
Якщо SeleniumHelper
або LetsShopHeaderSection
в основному визначають контракти методів без реалізації, розгляньте можливість використання інтерфейсів замість абстрактних чи конкретних класів.
Java дозволяє класу реалізовувати кілька інтерфейсів.
public interface SeleniumHelperInterface {
void someCommonMethod();
}
public class SeleniumHelper implements SeleniumHelperInterface {
protected WebDriver driver;
public SeleniumHelper(WebDriver driver) {
this.driver = driver;
}
@Override
public void someCommonMethod() {
// Реалізація
}
}
Тепер, клас LetsShopListingPage
може реалізовувати інтерфейси за потребою.
Який підхід обрати?
- Використовуйте ланцюг наслідування, якщо ієрархія чітка і прямолінійна.
- Використовуйте композицію, якщо вам потрібна більша гнучкість або якщо ви хочете уникнути тісного зв'язку між класами.
- Використовуйте інтерфейси, якщо ваші класи в основному визначають поведінку, а не стан.
Висновок
Модель одиничного наслідування в Java може здаватися обмеженою, але з гарним розумінням альтернатив, таких як ланцюги наслідування, композиція та інтерфейси, ви можете проектувати гнучкі та багаторазові структури коду. Дотримуючись цих принципів, ви уникнете помилок типу "Синтаксична помилка на токенах" і створите підтримувані та масштабовані додатки.
Який підхід вам більше підходить для обробки наслідування в Java? Поділіться своїми думками в коментарях нижче!
Щасливого кодування 💻
Перекладено з: Understanding the Java Inheritance Error: Why Extending Multiple Classes Fails and How to Fix It