Dog*_*Dog 13 java inheritance multiple-inheritance diamond-problem
在英语中,同形异义词对是两个具有相同拼写但含义不同的单词.
在软件工程中,一对同形方法是两种具有相同名称但要求不同的方法.让我们看一个人为的例子,让问题尽可能清晰:
interface I1 {
/** return 1 */
int f()
}
interface I2 {
/** return 2*/
int f()
}
interface I12 extends I1, I2 {}
Run Code Online (Sandbox Code Playgroud)
我该如何实施I12?C#有办法做到这一点,但Java没有.所以唯一的方法是破解.怎样才能最可靠地使用反射/字节码技巧/等(也就是说它不一定是一个完美的解决方案,我只想要一个效果最好的解决方案)?
请注意,我无法合法反向工程的一些现有的闭源大量遗留代码需要一个类型参数,I12并将I12两者都委托给I1作为参数的代码和作为参数的代码I2.所以基本上我需要让它的一个实例I12知道什么时候它应该起作用I1,什么时候它应该起作用I2,我相信可以通过在直接调用者的运行时查看字节码来完成.我们可以假设调用者没有使用反射,因为这是简单的代码.问题是作者I12没想到Java会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();
}
Run Code Online (Sandbox Code Playgroud)
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();
}
}
Run Code Online (Sandbox Code Playgroud)
Set员工的世界可能是申请雇主的雇员.因此,当complement在该集合上调用时,所有现有员工都被解雇,而之前应用的所有其他员工都被雇用.
Artist代表艺术家,如音乐家或演员.艺术家有自我.当其他人赞美他时,这种自我会增加:
interface Artist {
/** Complements the artist. Increases ego. */
public void complement();
public int getEgo();
}
Run Code Online (Sandbox Code Playgroud)
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);
}
}
Run Code Online (Sandbox Code Playgroud)
ArtistSet只是一个Artist和一个Set:
/** A set of associated artists, e.g: a band. */
interface ArtistSet extends Set, Artist {
}
Run Code Online (Sandbox Code Playgroud)
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();
}
}
}
Run Code Online (Sandbox Code Playgroud)
一旦你尝试实现一个问题就会变得清晰ArtistSet:两个超级接口都指定complement应该做其他事情,所以你必须以complement同样的方式在同一个类中实现具有相同签名的两个方法.Artist.complement是一个同形异义词Set.complement.
新想法,有点乱……
public class MyArtistSet implements ArtistSet {
public void complement() {
StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace();
// the last element in stackTraceElements is the least recent method invocation
// so we want the one near the top, probably index 1, but you might have to play
// with it to figure it out: could do something like this
boolean callCameFromHR = false;
boolean callCameFromTheatre = false;
for(int i = 0; i < 3; i++) {
if(stackTraceElements[i].getClassName().contains("Theatre")) {
callCameFromTheatre = true;
}
if(stackTraceElements[i].getClassName().contains("HRDepartment")) {
callCameFromHR = true;
}
}
if(callCameFromHR && callCameFromTheatre) {
// problem
}
else if(callCameFromHR) {
// respond one way
}
else if(callCameFromTheatre) {
// respond another way
}
else {
// it didn't come from either
}
}
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
1976 次 |
| 最近记录: |