0

I'm using something a bit like the command pattern to create a class which implements a simple interface for a method I reuse frequently, but in different ways.

I was able to get it working using a couple different methods, but I prefer this one, courtesy of Jon Skeet.

public class InterfaceProblemExample implements Runnable{

    public static void main(String... args) {
        new InterfaceProblemExample().run();
    }

    @Override
    public void run() {
        int dim = 3;
        final double[][] oldMatrix = new double[dim][dim];
        final double[][] newMatrix = new double[dim][dim];

        final AtomicReference<Looper> wrapper = new AtomicReference<Looper>();
        Looper looper = new Looper(dim, new Commandable() {
            @Override
            public void execute() {
                int axis1 = wrapper.get().getiAxis1();
                int axis2 = wrapper.get().getiAxis2();
                newMatrix[axis1][axis2] = oldMatrix[axis1][axis2] + 2.5;
            }
        });
        wrapper.set(looper);
        looper.execute();
    }

    public interface Commandable {
        public abstract void execute();
    }

    public class Looper implements Commandable {

        private Commandable command;
        private int iAxis1;
        private int iAxis2;
        private int dim;

        public Looper(int dim, Commandable command) {
            this.command = command;
            this.dim = dim;
        }

        public void setCommand(Commandable command) {
            this.command = command;
        }

        @Override
        public void execute() {
            for (iAxis2 = 1; iAxis2 < dim; iAxis2++) {
                for (iAxis1 = 0; iAxis1 < iAxis2; iAxis1++) {
                    command.execute();
                }
            }
        }

        public int getiAxis1() {
            return iAxis1;
        }

        public int getiAxis2() {
            return iAxis2;
        }

        public int getDim() {
            return dim;
        }
    }
}
Community
  • 1
  • 1
michaelsnowden
  • 6,031
  • 2
  • 38
  • 83
  • 1
    you should include what language / platform this is, many people might skip answering this just because of that ambiguity since there many languages that look similar these days. – osirisgothra Jan 04 '14 at 09:25

1 Answers1

1

You've effectively got a cyclic dependency: your Looper needs to be initialized with the Commandable, and the Commandable needs to be initialized with the Looper. The compiler is absolutely right to complain - imagine if the Looper constructor called command.execute() - that would try to use the looper variable (within the anonymous inner class) before you initialize it.

You need to break that cycle. For example:

  • You could make the Looper have a setCommandable method
  • You could make your Commandable implementation have a setLooper method
  • You could pass the Looper into the execute method

As a really hacky way round this, you could use an array or an atomic reference as a "wrapper":

public void run() {
    final AtomicReference<Looper> wrapper = new AtomicReference<Looper>();
    Looper looper = new Looper(new Commandable() {
        @Override
        public void execute() {
            System.out.println("This is easy: ");
            System.out.println("This isn't easy: " + wrapper.get().getI());
        }
    });
    wrapper.set(looper);
    looper.execute();
}

But that's pretty nasty.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • Your first two answers made perfect sense, and fixed my problem immediately. – michaelsnowden Jan 04 '14 at 09:39
  • I don't get how the third one would be implemented, but that last one is amazing to look at. Thanks so much for that Jon. This is exactly the kind of answer I was looking for. I'm going to have a lot of fun trying to understand this – michaelsnowden Jan 04 '14 at 09:40
  • @doctordoder: The third approach would need `Commandable` to be changed. For example, it could be generic in the type of object it needs to work on. Let me know if you want the array equivalent of the last one instead. – Jon Skeet Jan 04 '14 at 09:41
  • If you give me 5 minutes, I'll show you what exactly I'm needing. – michaelsnowden Jan 04 '14 at 09:43
  • Alright, I'm locked out of GitHub for some reason, but I edited the original question to include a version of what I'm actually trying to do. – michaelsnowden Jan 04 '14 at 10:05
  • @doctordoder: I suggest you revert the question, as now it's got *working* code which claims not to work. So long as the answer has helped you, that's probably all we need :) Let me know if there's anything more you need though. – Jon Skeet Jan 04 '14 at 10:07