如何在Java中动态创建对象并添加到ArrayList中

4
我目前正在使用Java和Codename One构建一个2D小行星游戏,强调面向对象编程模式。然而,我遇到了一个问题,就是如何动态地创建新对象(在我的情况下,我想添加的对象是一个超类GameObject),并将其添加到ArrayList中。在GameWorld.java的void init()方法中,您可以看到我创建了一个列表,其中包含GameObject的类型,其中包括Asteroids、Ships和Spacestations的子类。
该程序会在类中要求输入键盘命令,例如‘a’,然后应将一个新的Asteroid(GameObject的子类)对象添加到ArrayList中。用户应该能够添加尽可能多的小行星以分配到ArrayList中。
我的问题是:我如何在不像我目前在GameWorld类的addAsteroid()函数中已经声明名为AsteroidTest的类型为Asteroid的变量的情况下完成这个任务?谢谢!
package com.mycompany.a1;

import java.util.ArrayList; //For ArrayList Usage

public class GameWorld {

public void init() {
    ArrayList<GameObject> list = new ArrayList<GameObject>();
}
//other methods here to manipulate Game objects and data
public void addShip() {
    Ship ShipTest = new Ship();  

    list.add(ShipTest);

    System.out.println(ShipTest.getLocation());
}

public void addAsteroid(){
    Asteroid AsteroidTest = new Asteroid();

    list.add(AsteroidTest);


    System.out.println(AsteroidTest.getLocation());
}


public void addSpaceStation(){

}
}

Game.java

package com.mycompany.a1;

import com.codename1.ui.events.ActionListener;
import com.codename1.ui.Label;
import com.codename1.ui.TextField;
import com.codename1.ui.events.ActionEvent;
import java.lang.String;
import com.codename1.ui.Form;

public class Game extends Form{
private GameWorld gw;

public Game (){
    gw  = new GameWorld();
    gw.init();
    play();

}

private void play(){

    Label myLabel=new Label("Enter a Command:"); this.addComponent(myLabel);
    final TextField myTextField=new TextField();
    this.addComponent(myTextField);
    this.show();
    myTextField.addActionListener(new ActionListener(){
        public void actionPerformed(ActionEvent evt) {
            String sCommand=myTextField.getText().toString();
            myTextField.clear();
            switch (sCommand.charAt(0)){
            case 's':
                gw.addShip();
                break;
            case 'a':
                gw.addAsteroid();
                break;
            case 'b':
                gw.addSpaceStation();
                break;
            //add code to handle rest of the commands
            } //switch
        } //actionPerformed
    } //new ActionListener()
            ); //addActionListener
    } //play
  //code to enter text field and receive keyboard input 
}

GameObject.java是一个超类,包括了Asteroids(小行星)、Ships(飞船)、Missiles(导弹)和SpaceStation(空间站)的子类。

package com.mycompany.a1;

public abstract class GameObject {
private double x;
private double y;

public GameObject(){
    x = 0;
    y = 0;
}

public double getX(){
    return x;
}

public void setX(double newX){
    x = newX;
}

public double getY(){
    return y;
}

public void setY(double newY){
    y = newY;
}

public String getLocation(){
    return "(" + x + ", " + y + ")";

 }
}

Asteroid.java

 package com.mycompany.a1;

 import java.util.Random;

 public class Asteroid extends MovableObject {

 private Random rand = new Random();


 public Asteroid() {
    setX(rand.nextInt(1023) + rand.nextDouble());

    setY(rand.nextInt(767) + rand.nextDouble());// TODO Auto-generated 
  constructor stub
  }

 }

1
list.add(new Asteroid()); - Kevin Anderson
@KevinAnderson 哇,真的这么简单吗?我在 ArrayList 列表变量上也遇到了一个错误。错误提示说“未使用本地变量列表的值”。 - Ben Yan
应该提示“未定义list”。你在init()方法中本地定义了list,但是该定义在init()之外不可见。你希望将list定义为类的成员变量。如果你不明白原因,那么你需要学习的主题是“变量的作用域”。 - Kevin Anderson
啊,我不知道为什么我在那个方法中定义了列表,我将其声明为类的成员变量,现在一切都正常了。非常感谢你,Kevin,希望我能给你点赞! - Ben Yan
1个回答

1
很简单:

它相当简单:

public void addAsteroid(){
  this.list.add(new Asteroid());
}

但是如果你想要打印位置,你必须在创建新的小行星时进行,因此在构造函数中:

public GameObject(){
  x = 0;
  y = 0;
  System.out.println("(" + x + ", " + y + ")");
}

此外:
  • ArrayList<GameObject> list声明为List<GameObject>,使用抽象而不是具体实现
  • 在GameWorld类中将List<GameObject> list声明为私有字段
  • 考虑使用构造函数而不是public void init()
  • 如果存在并发问题,请使用java.util.concurrent.CopyOnWriteArrayList<E>而不是传统的List
  • 考虑使用GameObject的第二个构造函数来处理带有初始坐标的对象创建,并删除setX和setY方法,引入moveTo(double x, double y)方法以更清晰地表达意图

@maciejlesiak 谢谢你的建议!然而,我不理解你的第一点;使用 List<GameObject> 相对于使用 ArrayList<GameObject> List 的确切优势是什么?我肯定会添加第二个构造函数来处理 X 和 Y 坐标,这比我目前拥有的更加智能。 - Ben Yan
通常使用抽象(接口,抽象类等)而不是特定的实现作为变量类型是一个常见的做法。这样,您可以隐藏实现(因此您类的客户端不关心您在实现中是否使用ArrayList、LinkedList或其他List)。最重要的是,您应该使用某个list,因此应该有一个接口。如果您想了解更多信息,请参考SOLID原则,特别是DIP https://en.wikipedia.org/wiki/Dependency_inversion_principle - maciejlesniak

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接