На английском языке пара гомографов - это два слова, которые имеют одинаковое правописание, но разные значения.
В разработке программного обеспечения пара гомографических методов - это два метода с одним и тем же именем, но с разными требованиями. Посмотрим на надуманный пример, чтобы сделать вопрос максимально ясным:
interface I1 {
/** return 1 */
int f()
}
interface I2 {
/** return 2*/
int f()
}
interface I12 extends I1, I2 {}
Как я могу реализовать I12
? С# имеет способ сделать это, но Java нет. Таким образом, единственный способ - это взломать. Как это можно сделать с помощью трюков reflection/bytecode/etc наиболее надежно (т.е. Это не должно быть идеальным решением, я просто хочу, чтобы тот работал лучше всего)?
Обратите внимание, что какой-то существующий массив массивных фрагментов с закрытым исходным кодом, который я не могу законно развернуть, требует параметра типа I12
и делегирует I12
как код, который имеет I1
в качестве параметра, так и код, который имеет I2
в качестве параметра. Поэтому в основном мне нужно создать экземпляр I12
, который знает, когда он должен действовать как I1
, и когда он должен действовать как I2
, который, я считаю, может быть сделан на байт-код во время выполнения непосредственного вызывающего абонента. Мы можем предположить, что вызывающие не используют никакого отражения, потому что это простой код. Проблема в том, что автор I12
не ожидал, что Java слияния f
с обоих интерфейсов, так что теперь я должен придумать лучший взломать проблему. Ничто не вызывает I12.f
(очевидно, если автор написал код, который фактически называет I12.f
, он бы заметил проблему перед ее продажей).
Обратите внимание, что я действительно ищу ответ на этот вопрос, а не как изменить структуру кода, который я не могу изменить. Я ищу лучшее эвристическое возможное или точное решение, если оно существует. См. "Ответ на серый" для допустимого примера (я уверен, что есть более надежные решения).
Здесь - конкретный пример того, как может возникнуть проблема гомографических методов в двух интерфейсах. И вот еще один конкретный пример:
У меня есть следующие 6 простых классов/интерфейсов. Это напоминает бизнес вокруг театра и художников, которые выступают в нем. Для простоты и быть конкретным предположим, что все они созданы разными людьми.
Set
представляет собой набор, как в теории множеств:
interface Set {
/** Complements this set,
i.e: all elements in the set are removed,
and all other elements in the universe are added. */
public void complement();
/** Remove an arbitrary element from the set */
public void remove();
public boolean empty();
}
HRDepartment
использует Set
для представления сотрудников. Он использует сложный процесс для декодирования, который сотрудники нанимают/стреляют:
import java.util.Random;
class HRDepartment {
private Random random = new Random();
private Set employees;
public HRDepartment(Set employees) {
this.employees = employees;
}
public void doHiringAndLayingoffProcess() {
if (random.nextBoolean())
employees.complement();
else
employees.remove();
if (employees.empty())
employees.complement();
}
}
Вселенная a Set
сотрудников, вероятно, будет сотрудниками, которые обратились к работодателю. Поэтому, когда на этот набор вызывается complement
, все существующие сотрудники увольняются, а все остальные, которые применялись ранее, нанимаются.
Artist
представляет художника, такого как музыкант или актер. У художника есть эго. Это эго может возрасти, когда другие его комплимент:
interface Artist {
/** Complements the artist. Increases ego. */
public void complement();
public int getEgo();
}
Theater
выполняет операцию Artist
, что, возможно, приводит к дополнению Artist
. Аудитория театра может судить художника между выступлениями. Чем выше эго исполнителя, тем больше вероятность, что зрителям понравится Artist
, но если эго выходит за определенную точку, художник будет восприниматься зрителями отрицательно:
import java.util.Random;
public class Theater {
private Artist artist;
private Random random = new Random();
public Theater(Artist artist) {
this.artist = artist;
}
public void perform() {
if (random.nextBoolean())
artist.complement();
}
public boolean judge() {
int ego = artist.getEgo();
if (ego > 10)
return false;
return (ego - random.nextInt(15) > 0);
}
}
ArtistSet
- это просто Artist
и a Set
:
/** A set of associated artists, e.g: a band. */
interface ArtistSet extends Set, Artist {
}
TheaterManager
запускает показ. Если зрители театра ненавидят художника, театр беседует с отделом кадров, который, в свою очередь, будет стрелять артистами, нанимать новых и т.д.:
class TheaterManager {
private Theater theater;
private HRDepartment hr;
public TheaterManager(ArtistSet artists) {
this.theater = new Theater(artists);
this.hr = new HRDepartment(artists);
}
public void runShow() {
theater.perform();
if (!theater.judge()) {
hr.doHiringAndLayingoffProcess();
}
}
}
Проблема становится понятной, когда вы пытаетесь реализовать ArtistSet
: оба суперинтерфейса указывают, что complement
должен делать что-то еще, поэтому вам нужно реализовать два метода complement
с одной и той же сигнатурой внутри одного класса. Artist.complement
является гомографом Set.complement
.