【问题标题】:Using a variable to call a new instance of a class使用变量调用类的新实例
【发布时间】:2013-12-05 12:11:57
【问题描述】:

所以,这可能是也可能不是您的想法,我不完全确定我的问题措辞是否正确,但我们开始吧!我正在构建一个国际象棋游戏,并且我试图在如何调用我的类、构建棋子并将它们放在棋盘上时保持动态。

代码分析:我读了一个文件,其中包含一个片段,例如“pla1”,代表一个片段、它的颜色、它的 x 坐标和它的 y 坐标。在这个例子中,p = pawn,l = white,a = 棋盘上的 x 坐标,1 = 棋盘上的 y 坐标。所以:

“白兵在 a1 上”

简单。现在,我有一个从文件中解析棋子的棋子类,以及一个名为“Pawn”的特定类,所以当我想向棋盘添加棋子时,该棋子有自己的类来控制其移动能力和电源棋盘等等。

问题:我不知道如何动态设置我的作品类别。我可以使用硬编码版本轻松做到这一点,正如您将在 cmets 中看到的那样,或者使用 if陈述。是否可以使用我的字符串变量设置我的类?

我的代码如下:

//reads from the file, puts each line into an arraylist
public void readFromFile(String fileName) throws IOException 
{
    String line;
    BufferedReader br = new BufferedReader(new FileReader(fileName));

    //adds line to arraylist    
    while((line = br.readLine()) != null) 
       fileLines.add(line.toLowerCase());

    //send each line in the list to method
    for(String item : fileLines) 
        buildPiecesFromFileAndGetLocation(item);
} 

ChessPiece[][] pieces = new ChessPiece[9][9]; my chessboard
String spawnPiece;
String spawnColor;
String pieceRepresentation;

String originx;
int originX;
int originY;


public void buildPiecesFromFileAndGetLocation(String item)
{
    //regex matcher // using this to match it: Pattern copyPiece = Pattern.compile("(?<piece>q|k|b|p|n|r+)(?<color>l|d)(?<x>\\w)(?<y>\\d)");
    Matcher copyMatcher = copyPiece.matcher(item);

    //hashmap for matching quicker; currently only has pieceMatches.put("p", "Pawn") within it      
    hashIdentities();

    if (copyMatcher.find())
    {

        spawnPiece = pieceMatches.get(copyMatcher.group("piece")); //spawnPiece becomes "Pawn"
        spawnColor = colorMatches.get(copyMatcher.group("color"));
        pieceRepresentation = spawnPiece + spawnColor;

        originx = copyMatcher.group("x"); //change letter to number 
        transferChars(); //changes letter "a" into integer "1"
        originY = Integer.parseInt(copyMatcher.group("y")); //string to int

        //PROBLEM:
        pieces[originX][originY] = new spawnPiece(pieceRepresentation); //since spawnPiece is now "Pawn" i want it to be able to call a new instance of the class Pawn. this way doesn't work. Solution?

        //logic:
        //pieces[a][b] = new (WHATEVER PIECE WAS MATCHED)("position + color");

        //hardcoded version:
        //pieces[1][1] = new Pawn("PawnWhite");

      }
}

我不确定这是否可行。任何帮助将不胜感激,如果需要对代码进行任何详细说明,我可以提供。

【问题讨论】:

  • 你说“硬编码”好像它是某种邪恶……而“动态地做这个或那个”就好像它是某种最佳实践。 Java 是一种静态类型语言。如果你试图设计忽略这一事实的解决方案,那么你就太努力了,你最终会得到糟糕的代码。国际象棋游戏充满了静态类型的数据(这些棋子在编译时都是已知的)。我发现您坚持必须“动态地”执行此操作(这样您就没有编译时间类型检查和安全性的优势)是荒谬的。
  • 嗯,它只是我觉得很有用的东西,它是 OOP 的重要组成部分,能够动态地完成它。我宁愿以一种动态的方式来缩短我的代码。
  • 那么,另一种语言会更好地满足您的需求。 Java 中的反射代码很慢。看起来和阅读起来也非常冗长和丑陋。此外,当您包含所有异常处理时,您的代码几乎总是更长、更冗长。当存在静态类型的解决方案时,避免使用 Java 中的反射代码可为您提供卓越的安全性、更高的类型安全性、卓越的健壮性和可维护性以及更高的性能。使用静态类型环境的优势非常引人注目。
  • 为了更具体一点,如果您将棋子实现为 enum 类(并将玩家颜色作为策略枚举——J. Bloch,Effective Java,第二版。 ) 您可以定义常量特定方法来为您提供所需的所有特定于片段的行为......同时保持静态类型解决方案的稳健性和类型安全性。
  • 我明白了。在这种情况下,我只是想找到一个更动态的解决方案。谢谢

标签: java chess


【解决方案1】:

使用工厂类。

interface ChessPieceFactory {
  ChessPiece create(String pieceName);
}

class ChessPieceFactoryImpl {
  public ChessPiece create(String pieceName) {
    if("pawn".equals(pieceName))
      return new Pawn(pieceName);
    throw new IllegalArgumentException(pieceName);
  }
}

【讨论】:

  • OP 不想使用 Ifs
  • 他可以使用反射(如果他愿意,可以使用Constructor.newInstance()),但是工厂类是从主代码中删除硬编码并分离文件解析和对象创建的好方法。
  • 硬编码只有分散在代码周围才是邪恶的;使用 newInstance() 需要更改文件,因为他需要完整的类名来实例化对象;如果他想使用“pawn”,他需要一个外部(硬编码)文件将每个棋子名称映射到正确的类名
  • ...相当于把“p”翻译成“pawn”。
【解决方案2】:

看这篇文章:http://docs.oracle.com/javase/tutorial/reflect/member/ctorInstance.html

直接引用:

创建类实例有两种反射方法: java.lang.reflect.Constructor.newInstance() 和 Class.newInstance()。 前者是首选,因此在这些示例中使用,因为:

  1. Class.newInstance() 只能调用零参数的构造函数, 而 Constructor.newInstance() 可以调用任何构造函数,不管 参数的数量。
  2. Class.newInstance() 抛出任何异常 由构造函数抛出,无论是检查还是 未经检查。 Constructor.newInstance() 总是包装抛出的异常 带有 InvocationTargetException。
  3. Class.newInstance() 要求 构造函数可见; Constructor.newInstance() 可以调用 特定情况下的私有构造函数。

让我为你添加一些代码。首先,您需要获取对要创建其实例的类的引用。你已经有了班级的名字,所以这样做:

Class pieceKlass = Class.forName(spawnPiece)

然后得到它的构造函数,它接受一个字符串并创建一个类的实例:

Constructor ctor = pieceKlass.getDeclaredConstructor(String.class);
ctor.setAccessible(true);
ChessPiece piece = (ChessPiece)ctor.newInstance(pieceRepresentation);

鉴于这段代码非常复杂并且还需要一些错误处理,您现在可以将它巧妙地提取到工厂类中:

class ChessPieceFactory {
    public ChessPiece create(String pieceName, String pieceRepresentation) {
      ChessPiece piece;
      try {
        Class pieceKlass = Class.forName(pieceName)
        Constructor ctor = pieceKlass.getDeclaredConstructor(String.class);
        ctor.setAccessible(true);
        piece = (ChessPiece)ctor.newInstance(pieceRepresentation);
       // production code should handle these exceptions more gracefully
     } catch (ClassNotFoundException x) {
        x.printStackTrace();
     } catch (InstantiationException x) {
        x.printStackTrace();
     } catch (IllegalAccessException x) {
        x.printStackTrace();
     } catch (InvocationTargetException x) {
        x.printStackTrace();
     } catch (NoSuchMethodException x) {
        x.printStackTrace();
     }

     return piece;
  }

由于您还使用 Chess 标签标记了问题,我应该指出您应该使用标准 Forsyth-Edwards Notation 来填充棋盘。它用大写字母表示白色棋子,用小写字母表示黑色棋子。

【讨论】:

  • 感谢您为我指明正确的方向。仍在搞砸它,但它可以这样做。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-07-05
  • 1970-01-01
  • 2019-02-01
  • 2021-05-08
  • 2018-06-23
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多