我的业余时间正在研究一个小的Altair模拟器。
经过很长时间的C#工作后,我决定再次尝试使用Java,所以请原谅我的主要愚蠢行为。

本质上,我使用命令模式和org.reflections来简化机器指令的实现。这是我的反射代码。

        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;

public interface IInstruction{
    byte OpCode = 0;
    String Mnemonic = "XXX";
    void Execute();
}


这是一个具体的实现:

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(){
    }
}


这是奇怪的地方。我使用OpCode来遍历所有指令,并且当我将执行的OpCode与反映的指令进行比较时,即使我不是说super,该比较也使用超类的默认操作码(0)。

IntelliJ中的效果甚至更奇怪。将鼠标悬停在上面可以显示超级操作码,但是深入研究指令集可以看到正确的具体操作码。这是一些奇怪的照片。

java - 遍历反射的子类,变量似乎没有阴影,IntelliJ似乎冲突-LMLPHP
java - 遍历反射的子类,变量似乎没有阴影,IntelliJ似乎冲突-LMLPHP
任何帮助将不胜感激,因为我正疯狂地迷路,这阻碍了任何前进的进展。谢谢。

编辑:刚在我的屏幕快照中注意到它说操作码= 0(用户参数)。请忽略此。通常是58岁,我只是跳过了一步来获取屏幕截图。

最佳答案

接口没有字段,“字段” IInstructions.OpCode是一个常量(隐式public static final),而LDAInstruction.OpCode是一个字段,与给定的代码无关。使用IInstrunctions instructions时,表达式instructions.OpCode引用常量(请注意,以这种方式引用静态成员被认为是一种不好的做法,大多数IDE和静态分析器都会对此发出警告)。

这也解释了IntelliJ显示的值:在循环中,代码引用了IInstructions.OpCode,因此它显示了值0,而在检查实际对象时,它知道它是LDAInstruction的实例,其中包含一个值为opCode的字段58

IntelliJ对代码的渲染也对此提供了提示:紫色斜体渲染是一个静态字段,而紫色“正常”(非斜体)渲染是一个实例字段。

您应该从接口中删除OpCode,而是声明一个getter(byte getOpCode(),然后子类需要实现该getter以返回该指令的正确代码。

我还建议您熟悉Java命名约定:一个常数全为大写(例如OP_CODE),而字段以小写字母开头(例如opCode),这些字段通常不是public,并且在在这种情况下,它可能应该是一个最终字段,因此不能(偶然或有意)对其进行修改。

您还应该考虑将其实现为枚举,因为从技术上讲,您只需要一个指令的单个实例,例如

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;
    }

}


在这种情况下,您可以使用enum name()方法,而不是定义单独的助记符字段+ getter。您可以使用枚举静态方法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;
   }
}

10-06 07:24