При встраивании двух типов с одноимённым методом на одном уровне какой вызов станет неоднозначным?
Вызов одноимённого метода через внешний тип станет неоднозначным, если оба метода продвигаются из встроенных полей на одинаковой глубине. Компилятор отвергнет такой селектор; необходимо явно указать, через какое встроенное поле вызывается метод.
Если одноимённый метод объявлен непосредственно у внешней структуры, он имеет приоритет над продвигаемыми методами. Если методы находятся на разной глубине встраивания, выбирается метод с меньшей глубиной.
Встраивание типов в Go предназначено для композиции и повторного использования поведения без классического наследования. Оно позволяет обращаться к методам встроенного типа через внешний тип, создавая удобный механизм продвижения методов.
При этом продвижение не устраняет структуру исходных типов. Если один селектор может означать разные методы, Go не выбирает вариант эвристически, чтобы поведение не зависело от случайной структуры программы.
Неоднозначный вызов опасен тем, что внешне выглядит как обычный вызов метода, но не имеет единственного получателя. Молчаливый выбор одного из методов мог бы привести к незаметной ошибке после добавления нового встроенного типа или метода.
Проблема возникает только при обращении через неоднозначное имя. Вызов через конкретное встроенное поле остаётся возможным, поскольку путь к методу задан явно.
Здесь обращение к s.Write() было бы ошибкой компиляции, а явно квалифицированные вызовы однозначны.
При поиске селектора Go сначала рассматривает методы самого внешнего типа, затем продвигаемые методы встроенных полей по уровням вложенности. Метод, найденный на меньшей глубине, скрывает одноимённые методы на большей глубине.
Если на минимальной глубине найдено несколько методов с одним именем, селектор считается неоднозначным. Компилятор не использует сигнатуру метода для выбора и не пытается определить намерение разработчика.
Однозначность метода и совместимость с интерфейсом связаны, но не идентичны. Если продвигаемый метод исключён из-за конфликта имён, внешний тип не получает через это имя корректного метода; поэтому он может перестать удовлетворять интерфейсу с таким методом.
Обычно применяют один из вариантов:
Явная квалификация сохраняет простой дизайн, но оставляет выбор реализации в местах вызова. Метод-обёртка лучше скрывает внутреннюю композицию, однако добавляет код и требует определить правила объединения или приоритета поведения.
Сервис встраивает два компонента: журналирование и сбор метрик. Оба предоставляют метод Write, но запись в журнал и отправка метрики имеют разный смысл.
Вариант с вызовом через внешний сервис не подходит: он приводит к ошибке неоднозначности. Вызов через конкретное поле технически прост, но распространяет детали внутренней структуры по всему коду. Переименование методов в исходных компонентах может быть невозможно, если они находятся в сторонних пакетах.
Практичное решение — объявить у сервиса собственный метод с предметным именем, например методом записи события, и внутри явно вызвать нужные методы компонентов. Так внутреннее встраивание остаётся скрытым, а публичный контракт сервиса становится однозначным. Цена решения — дополнительная обёртка, которую нужно поддерживать при изменении политики записи.
Нет. Если один метод продвигается непосредственно из поля внешней структуры, а другой — через дополнительный уровень встраивания, метод с меньшей глубиной скрывает более глубокий. Неоднозначность возникает среди кандидатов на одинаковой минимальной глубине.
Да. Неоднозначность относится к сокращённому селектору внешнего типа. Обращение через конкретное встроенное поле задаёт полный путь и потому однозначно. Это не устраняет конфликт в публичном API, но позволяет использовать обе реализации внутри кода.
Да. Для удовлетворения интерфейсу у типа должен существовать однозначный метод с требуемым именем и совместимой сигнатурой. Если метод не может быть выбран из-за конфликта на одном уровне, нельзя считать, что внешний тип получил такой метод посредством продвижения; обычно требуется определить собственный метод-обёртку или изменить композицию.