Перебирая отраженные подклассы, переменные не кажутся теневыми, IntelliJ кажется конфликтующим - PullRequest
0 голосов
/ 12 октября 2018

В свободное время я работаю над небольшим эмулятором Альтаира.Я решил снова попробовать работать в Java после ОЧЕНЬ долгого времени работы с C #, так что извините за любой серьезный идиотизм с моей стороны.

По сути, я использую шаблон команды и 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 с отраженными инструкциями, сравнение использует код операции по умолчанию для суперкласса (0), хотя я не говорю super.

Эффект в IntelliJ еще более странный.При наведении курсора мыши отображается супер-код операции, а при копании в коллекции инструкций отображается правильный конкретный код операции.Вот несколько фотографий этой странности.

Superclass on mouseover Subclass on drilldown Любая помощь будет принята, поскольку я просто безумно потерян, и это блокирует любого форвардапрогресс.Спасибо.

РЕДАКТИРОВАТЬ: только что заметил на моих скриншотах, что он говорит opcode = 0 (аргумент пользователя).Пожалуйста, игнорируйте это.Обычно это 58, я просто пропустил шаг, чтобы добраться до скриншотов.

1 Ответ

0 голосов
/ 12 октября 2018

Интерфейсы не имеют полей, 'field' IInstructions.OpCode является константой (неявным public static final), и хотя LDAInstruction.OpCode является полем, которое не имеет значения в данном коде.С IInstrunctions instructions выражение instructions.OpCode относится к константе (имейте в виду, что ссылаться на статические элементы таким способом считается плохой практикой, и большинство IDE и статических анализаторов будут предупреждать вас об этом).

Это также объясняет значение, показанное IntelliJ: в цикле код ссылается на IInstructions.OpCode, поэтому он показывает значение 0, при проверке фактического объекта он знает, что это экземпляр LDAInstruction, который содержитполе opCode со значением 58.

Визуализация кода IntelliJ также дает подсказку об этом: фиолетовый курсивный рендеринг является статическим полем, а фиолетовый «нормальный» (не курсив)) рендеринг - это поле экземпляра.

Вы должны удалить OpCode из интерфейса и вместо этого объявить получатель (byte getOpCode(), затем подклассы должны реализовать этот получатель, чтобы вернуть правокод для этой инструкции.

Я также предлагаю вам ознакомиться с соглашениями об именах 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()вместо определения отдельного мнемонического поля + геттер.И вы можете использовать статический метод enum valueOf(String) для получения экземпляров по имени.

Но в зависимости от размера реализаций execute и количества инструкций это может быть не лучшим решением.

Другой подход - использовать абстрактный класс вместо (или в дополнение к) интерфейса, например:

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