質問

I was working on a game recently in C++ where I implemented the Command Pattern to manage keyboard input to control a spaceship. When instantiating all the commands, I'd pass the spaceship pointer to each constructor so all commands were working with the same spaceship object.

This pattern made sense in C++ because you can pass by reference, but in Java everything is pass-by-value. If I tried to implement the same thing in Java, how would I have each command pointing to the same object?

In all the examples I've seen of the Command Pattern used in Java it makes no difference whether the member variables of each Command are copies or references.

役に立ちましたか?

解決

Java does pass by value in all cases, however all 'non-primitive' types exist as references, so for types like String, List or Spaceship you're actually passing a reference around - the pass by value is because you're passing the reference by value. Which is a pretty confusing way of expressing it, I always felt.

Anyway, the upshot of it is that if you have a (hugely simplified) Command class

public class Command {
    private Spaceship spaceship;
    public Command(Spaceship ship) {
        spaceship = ship;
    }
}

And then you do this:

Spaceship ship = new Spaceship();
List<Command> ships = new List<Command>();
for (int i = 0; i < 40; i++) {
    ships.add(new Command(ship));
}

then every single one of those Command objects has a reference to the same Spaceship object. Passing the reference by value does not cause a copy of the Spaceship object that the reference points to. However, if you were passing ints around, you would indeed be passing copies.

Just remember the distinction between primitive types like int, and object types like String and you'll be fine. It's helpful to remember that primitive type names begin with a lowercase letter (and can never be user-defined), while object types begin with a capital letter. All user-defined types are object types.

他のヒント

Thanks for your help guys. At the end of the day it came down to my complete misunderstanding about how Java works. I was under the impression (for some strange reason) that creating a Command and giving it my object meant that it received a copy instead of a reference to the original. If that was the case then calling .execute() in a Command would have had no effect on the object outside of the class.

Yet, I found that this was not the case after creating a small test:

Sprite.java:

public class Sprite {
    private int x;

    Sprite(int x) {
        this.x = x;
    }

    public int getX() {
        return x;
    }

    public void setX(int x) {
        this.x = x;
    }
}

Command.java:

public interface Command {
    void execute();
}

MoveLeftCommand.java:

public class MoveLeftCommand implements Command {
    private Sprite s;

    MoveLeftCommand(Sprite s) {
        this.s = s;
    }

    public void execute() {
        s.setX(s.getX() - 1);
    }
}

MoveRightCommand.java:

public class MoveRightCommand implements Command {
    private Sprite s;

    MoveRightCommand(Sprite s) {
        this.s = s;
    }

    public void execute() {
        s.setX(s.getX() + 1);
    }
}

Test.java:

import java.lang.*;
import java.util.*;

public class Test
{    
    public static void main(String[] args) {
        Sprite mario = new Sprite(0);
        Command command = null;

        Map<String, Command> commands = new HashMap<String, Command>();
        commands.put("a", new MoveLeftCommand(mario));
        commands.put("d", new MoveRightCommand(mario));

        // Test...
        System.out.println(mario.getX()); // 0

        command = (Command) commands.get("a");

        command.execute();
        System.out.println(mario.getX()); // -1
        command.execute();
        System.out.println(mario.getX()); // -2

        command = (Command) commands.get("d");

        command.execute();
        System.out.println(mario.getX()); // -1
    }
}

I correctly saw 0 -1 -2 -1 in the console, just as I would have in C++.

In java they passed "by value"... You pass real object that will implement an execute method. Sharing reference among different objects can be achieved by proper OO design or lookup method injection for common attribute. The Command pattern example was taken from wikipedia

/*the Command interface*/
public interface Command {
   void execute();
}


/*the Invoker class*/
import java.util.List;
import java.util.ArrayList;

public class Switch {

   private List<Command> history = new ArrayList<Command>();

   public Switch() {
    }

   public void storeAndExecute(Command cmd) {
      this.history.add(cmd); // optional 
      cmd.execute();        
   }

}


/*the Receiver class*/
public class Light {

   public Light() {
   }

   public void turnOn() {
      System.out.println("The light is on");
   }

   public void turnOff() {
      System.out.println("The light is off");
   }

}


/*the Command for turning on the light - ConcreteCommand #1*/
public class FlipUpCommand implements Command {

   private Light theLight;

   public FlipUpCommand(Light light) {
      this.theLight = light;
   }

   public void execute(){
      theLight.turnOn();
   }

}


 }

Since Java always does pass by value, you will need to do something mutable to pass through instead (pass a mutable object).

Consider this example:

In this agnostic language that supports pass-by-reference

string commandArg = "";
new StringCommand().execute(commandArg);
Console.write(commandArg);

And in StringCommand.execute(string arg), you have arg += "Hello World", then your output will be Hello World.

In Java, simply pass a StringBuilder as follows:

StringBuilder sb = new StringBuilder();
new StringCommand().execute(sb);
System.out.println(sb.toString());

where StringCommand.execute(StringBuilder sb) contains:

sb.append("Hello World");

The StringBuilder reference is passed by value in StringCommand.execute. In a nutshell, primitive types are passed by value and, for objects, the reference of the object is passed by value.

I hope this helps.

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top