Java 迭代反射的子类,变量don';I don’’我看起来不太明白,IntelliJ似乎很矛盾

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");

我在业余时间做一个牛郎星模拟器。 在使用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;

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

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