在英语中,同形异义词对是拼写相同但含义不同的两个单词。
在软件工程中,一对单应方法是指名称相同但需求不同的两种方法。让我们看一个人为的例子,以使问题尽可能清楚:
interface I1 {
/** return 1 */
int f()
}
interface I2 {
/** return 2*/
int f()
}
interface I12 extends I1, I2 {}
我该如何实施I12
?C#有办法做到这一点,但 Java 没有。所以唯一的办法就是破解。如何最可靠地使用反射/字节码技巧/等来完成它(即它不必是一个完美的解决方案,我只想要一个效果最好的解决方案)?
请注意,我无法合法进行逆向工程的一些现有的封闭源大量遗留代码需要一个类型参数,并将两者I12
委托给具有作为参数的代码和具有作为参数的代码。所以基本上我需要创建一个知道它何时应该作为以及何时应该作为的实例,我相信这可以通过在直接调用者的运行时查看字节码来完成。我们可以假设调用者没有使用反射,因为这是简单的代码。问题是作者没想到Java会从两个接口合并,所以现在我必须想出解决这个问题的最佳方法。没有什么叫I12
I1
I2
I12
I1
I2
I12
f
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();
}
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
和一个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
。