在英语中,同形异义词指的是拼写相同但意思不同的两个单词。
在软件工程中,同形异义方法指的是具有相同名称但要求不同的两种方法。接下来通过一个人为例子来更加清晰地阐述这一问题:
interface I1 {
/** return 1 */
int f()
}
interface I2 {
/** return 2*/
int f()
}
interface I12 extends I1, I2 {}
如何实现 I12
接口?C# 中有一种方式可以实现,但是 Java 没有。所以唯一的方法是通过一个 hack 来解决。如何通过反射/字节码技巧等方式 最可靠地 实现它(即它不必是完美的解决方案,我只需要最好的方案)?
请注意,某些现有的闭源大型遗留代码需要一个类型为 I12
的参数,并将 I12
委托给具有 I1
参数和 I2
参数的代码。因此,基本上我需要创建一个知道何时应该作为 I1
和何时应该作为 I2
行事的 I12
实例,这可以通过在运行时查看立即调用者的字节码来完成。我们可以假设调用者不使用反射,因为这是直接的代码。问题在于 I12
的作者没有预料到 Java 会合并两个接口中的 f
,所以现在我必须想出一个最好的 hack 来解决问题。没有任何方法调用 I12.f
(显然,如果作者编写了实际调用 I12.f
的代码,他在销售之前就会注意到这个问题)。
请注意,我实际上正在寻找答案,而不是如何重构我无法更改的代码。我正在寻找可能的最佳启发式或确切的解决方案(如果存在)。有关有效示例,请参见 Gray 的回答(我确信还有更健壮的解决方案)。
这里是两个接口中同名方法可能导致的问题的具体示例。这里是另一个具体示例:
我有以下 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();
}
}
Set
中的员工集合可能是已经向雇主申请的员工。因此,当在该集合上调用complement
时,所有现有的员工都将被解雇,所有之前申请过的其他人将被雇佣。
Artist
代表艺术家,例如音乐家或演员。艺术家有自我意识。当他得到他人的赞美时,这种自我意识会增强:
interface Artist {
/** Complements the artist. Increases ego. */
public void complement();
public int getEgo();
}
剧场
让一个艺术家
表演,这可能会导致艺术家
受到赞扬。观众可以在演出之间评判艺术家。表演者的自我意识越高,观众就越喜欢艺术家
,但如果自我意识超过一定程度,观众就会对艺术家产生负面看法:
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
和一个 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
的同形异义词。
Artist
接口的方法使用了错误的单词,应该是拼写错误了。Artist
的方法应该是compliment()
,其中有一个字母"i"。实际上,这两个complement
方法的含义对应于英语中的_同音异义词_,即Set
中的"complement"和Artist
中的"compliment"。 - rgettman