我最近在用 C++ 开发一个游戏,我在其中实现了命令模式来管理键盘输入以控制宇宙飞船。在实例化所有命令时,我会将飞船指针传递给每个构造函数,以便所有命令都使用同一个飞船对象。
这种模式在 C++ 中是有意义的,因为您可以通过引用传递,但在 Java 中,一切都是按值传递。如果我尝试在 Java 中实现相同的东西,我将如何让每个命令指向同一个对象?
在我看到的所有关于 Java 中使用的命令模式的示例中,每个命令的成员变量是副本还是引用都没有区别。
我最近在用 C++ 开发一个游戏,我在其中实现了命令模式来管理键盘输入以控制宇宙飞船。在实例化所有命令时,我会将飞船指针传递给每个构造函数,以便所有命令都使用同一个飞船对象。
这种模式在 C++ 中是有意义的,因为您可以通过引用传递,但在 Java 中,一切都是按值传递。如果我尝试在 Java 中实现相同的东西,我将如何让每个命令指向同一个对象?
在我看到的所有关于 Java 中使用的命令模式的示例中,每个命令的成员变量是副本还是引用都没有区别。
Java 确实在所有情况下都按值传递,但是所有“非原始”类型都作为引用存在,因此对于 , 之类的类型String
,List
或者Spaceship
您实际上是在传递引用 - 按值传递是因为您正在按值传递引用. 这是一种非常令人困惑的表达方式,我一直觉得。
无论如何,它的结果是,如果你有一个(非常简化的)Command 类
public class Command {
private Spaceship spaceship;
public Command(Spaceship ship) {
spaceship = ship;
}
}
然后你这样做:
Spaceship ship = new Spaceship();
List<Command> ships = new List<Command>();
for (int i = 0; i < 40; i++) {
ships.add(new Command(ship));
}
那么这些对象中的每一个都有对同一个Command
对象的引用。按值传递引用不会导致引用指向的对象的副本。但是,如果您在传递s,那么您确实会传递副本。 Spaceship
Spaceship
int
int
只要记住原始类型 like和 object 类型之间的区别就String
可以了。记住原始类型名称以小写字母开头(并且永远不能由用户定义)是有帮助的,而对象类型以大写字母开头。所有用户定义的类型都是对象类型。
谢谢你们的帮助。归根结底,这归结为我对 Java 工作原理的完全误解。我的印象是(出于某种奇怪的原因)创建 aCommand
并将我的对象提供给它意味着它收到了一个副本而不是对原始对象的引用。如果是这种情况,那么调用.execute()
aCommand
对类外的对象没有影响。
然而,我在创建一个小测试后发现情况并非如此:
雪碧.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;
}
}
命令.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);
}
}
测试.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
}
}
我在控制台中正确地看到了 0 -1 -2 -1,就像在 C++ 中一样。
在java中,他们通过“按值”传递......您传递将实现执行方法的真实对象。不同对象之间的共享引用可以通过适当的面向对象设计或共同属性的查找方法注入来实现。命令模式示例取自维基百科
/*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();
}
}
}
由于 Java总是按值传递,因此您需要做一些可变的事情来传递(传递一个可变对象)。
考虑这个例子:
在这种支持传递引用的不可知语言中
string commandArg = "";
new StringCommand().execute(commandArg);
Console.write(commandArg);
而在StringCommand.execute(string arg)
,你有arg += "Hello World"
,那么你的输出将是Hello World
。
在 Java 中,只需传递 aStringBuilder
如下:
StringBuilder sb = new StringBuilder();
new StringCommand().execute(sb);
System.out.println(sb.toString());
其中StringCommand.execute(StringBuilder sb)
包含:
sb.append("Hello World");
StringBuilder
引用是按值传递的StringCommand.execute
。简而言之,原始类型是按值传递的,对于对象,对象的引用是按值传递的。
我希望这有帮助。