Java 迭代反射的子类,变量don';I don’’我看起来不太明白,IntelliJ似乎很矛盾
我在业余时间做一个牛郎星模拟器。 在使用C#很长时间之后,我决定再次尝试使用Java,所以请原谅我的任何重大愚蠢行为 本质上,我使用命令模式和org.reflections来简化机器指令的实现。这是我的反射代码Java 迭代反射的子类,变量don';I don’’我看起来不太明白,IntelliJ似乎很矛盾,java,inheritance,intellij-idea,reflection,shadowing,Java,Inheritance,Intellij Idea,Reflection,Shadowing,我在业余时间做一个牛郎星模拟器。 在使用C#很长时间之后,我决定再次尝试使用Java,所以请原谅我的任何重大愚蠢行为 本质上,我使用命令模式和org.reflections来简化机器指令的实现。这是我的反射代码 instructions = new ArrayList<IInstruction>(); Reflections reflections = new Reflections("com.cjm.eaglestar.instructions");
instructions = new ArrayList<IInstruction>();
Reflections reflections = new Reflections("com.cjm.eaglestar.instructions");
Set<Class<? extends IInstruction>> subTypesOf = reflections.getSubTypesOf(IInstruction.class);
for(Class<? extends IInstruction> command : subTypesOf){
try {
instructions.add(command.getDeclaredConstructor().newInstance());
}
catch(Exception e){
System.out.println(e.toString());
}
}
下面是一个具体的实现:
package com.cjm.eaglestar.instructions;
import static com.cjm.eaglestar.Eaglestar.machineState;
public class LDAInstruction implements IInstruction {
public byte OpCode = Byte.valueOf("00111010",2);
public String Mnemonic = "LDA";
public void Execute(){
machineState.programCounter++;
byte lowByte = machineState.memory[machineState.programCounter & 0xFF];
machineState.programCounter++;
byte highByte = machineState.memory[machineState.programCounter & 0xFF];
machineState.registers.A = machineState.memory[(lowByte & 0xFF) + ((highByte & 0xFF) * 256)];
}
public LDAInstruction(){
}
}
这就是事情变得奇怪的地方。我使用操作码对所有指令进行迭代,当我将执行的操作码与反映的指令进行比较时,比较使用超类的默认操作码(0),即使我不是说super。
IntelliJ中的效果更为奇怪。鼠标悬停显示超级操作码,但深入指令集合显示正确的具体操作码。这里有一些关于这种奇怪的照片
任何帮助都将不胜感激,因为我只是精神错乱,这阻碍了任何前进。谢谢
编辑:刚刚注意到在我的屏幕截图中,它说操作码=0(用户参数)。请忽略这一点。通常是58,我只是跳过了一步进入屏幕截图。界面没有字段,“字段”
I指令。操作码是一个常量(隐式公共静态final
),而LDAInstruction.OpCode
是一个字段,在给定代码中是无关的。对于iInstallations instructions
,表达式instructions.OpCode
引用常量(请注意,以这种方式引用静态成员被认为是一种不好的做法,大多数IDE和静态分析器都会对此发出警告)
这也解释了IntelliJ显示的值:在循环中,代码引用了IInstructions.OpCode
,因此它显示值0
,当检查实际对象时,它知道它是LDAInstruction
的一个实例,其中包含一个值为58
的字段OpCode
IntelliJ对代码的呈现也给出了一个提示:紫色斜体呈现是一个静态字段,而紫色“正常”(非斜体)呈现是一个实例字段
您应该从接口中删除操作码
,并声明一个getter(byte getoptcode()
),然后子类需要实现该getter以返回该指令的正确代码
我还建议您熟悉Java命名约定:常量应该是全大写的(例如OP\u code
),而字段以小写字母开头(例如opCode
),字段通常是而不是公共的
,在这种情况下,它可能应该是最后一个字段,所以不能是(意外地或有意地)修改
您还应该考虑将此实现为枚举,从技术上讲,您只需要一个指令的实例(如
)。
public enum Instruction {
LDA(Byte.valueOf("00111010",2)) {
@Override
public void execute(MachineState machineState) {
// your implementation.
}
},
// other instructions
;
private final byte opCode;
Instruction(byte opCode) {
this.opCode = opcode;
}
public abstract void execute(MachineState machineState);
public final byte getOpCode() {
return opCode;
}
}
在这种情况下,您可以使用enumname()
方法,而不是定义单独的助记符字段+getter。您还可以使用enum静态方法valueOf(String)
按名称获取实例
但根据执行实现的大小和指令的数量,这可能不是最佳解决方案
另一种方法是使用抽象类代替(或补充)接口,例如:
public abstract class AbstractInstruction {
private final byte opCode;
private final String mnemonic;
protected AbstractInstruction(byte opCode, String mnemonic) {
this.opCode = opCode;
this.mnemonic = mnemonic;
}
public abstract execute(MachineState machineState);
public final byte getOpCode() {
return opCode;
}
public final String getMnemonic() {
return mnemonic;
}
}
天啊。我真不敢相信我没有想到这是一个解决方案。我想我已经习惯了用C#自然而然地做这种事情。太感谢你了!@Mort432我在回答中没有明确提出的建议:在你的代码中machineState
似乎是一个公共静态(最终)字段。您应该重新考虑该设计选择,而是使用传递给execute
方法的对象。这将使您的代码更加清晰,并防止共享状态如果您希望能够并行运行多台计算机,它可能还将对(单元)进行状态管理作为旁注,测试非常容易,而不是像byte-OpCode=byte.valueOf(“00111010”,2);
这样昂贵的构造,您可以简单地使用byte-OpCode=0B001111010;
…
public abstract class AbstractInstruction {
private final byte opCode;
private final String mnemonic;
protected AbstractInstruction(byte opCode, String mnemonic) {
this.opCode = opCode;
this.mnemonic = mnemonic;
}
public abstract execute(MachineState machineState);
public final byte getOpCode() {
return opCode;
}
public final String getMnemonic() {
return mnemonic;
}
}