Java子类方法调用异常:棋子数组无法调用子类valid()方法
象棋程序移动合法性校验的类型转换问题
问题场景
我从零开发一款象棋程序,定义了父类Piece,以及Pawn、Bishop等各类棋子子类,Board类使用Piece[][]数组存储棋盘棋子信息。
棋盘初始化代码如下:
private Piece[][] board = new Piece[][]{ {new Rook(-1, 7,0),new Knight(-1, 7, 1),new Bishop(-1, 0, 0),new Queen(-1,7,3),new King(-1,7,4),new Bishop(-1,7,5),new Knight(-1,7,6),new Rook(-1,7,7)}, {new Pawn(-1,6,0),new Pawn(-1,6,1),new Pawn(-1,6,2),new Pawn(-1,6,3),new Pawn(-1,6,4),new Pawn(-1,6,5),new Pawn(-1,6,6),new Pawn(-1,6,7)}, {null,null,null,null,null,null,null,null}, {null,null,null,null,null,null,null,null}, {null,null,null,null,null,null,null,null}, {null,null,null,null,null,null,null,null}, {new Piece(1,1,1,0),new Pawn(1,1,1),new Pawn(1,1,2),new Pawn(1,1,3),new Pawn(1,1,4),new Pawn(1,1,5),new Pawn(1,1,6),new Pawn(1,1,7)}, {new Rook(1,0,0),new Knight(1,0,1),new Bishop(1,0,2),new Queen(1,0,3),new King(1,0,4),new Bishop(1,0,5),new Knight(1,0,6),new Rook(1,0,7)} };
棋子构造方法参数为(int Color, int Rank, int File),用整数类型存储Color是为了方便计算棋子价值。目前不想重构棋盘部分代码,但在移动合法性校验时遇到问题:
- 原本想让
Board类处理通用逻辑后,调用对应棋子子类的valid()方法,但直接调用时实际执行的是父类Piece的valid()方法。 - 尝试用switch+类型转换的方式调用子类方法,代码如下:
switch(board[7-rank][file].getType()){ case 1: Pawn p = (Pawn)board[7-rank][file];return p.valid(); case 2: Bishop b = (Bishop)board[7-rank][file];return b.valid(); case 3: Knight n = (Knight)board[7-rank][file];return n.valid(); case 4: Rook r = (Rook)board[7-rank][file];return r.valid(); case 5: Queen q = (Queen)board[7-rank][file];return q.valid(); case 6: King k = (King)board[7-rank][file];return k.valid(); default: System.out.println("Data returned as Piece, returning false.");return false; }
但运行时出现类型转换异常:
class Piece cannot be cast to class Pawn (Piece and Pawn are in unnamed module of loader com.sun.tools.javac.launcher.MemoryClassLoader @68b32e3e)
问题根源
- 棋盘初始化错误:在第6行(白方兵行)的第一个元素是
new Piece(1,1,1,0),这是父类Piece的实例,而非Pawn子类实例。当getType()返回1(代表Pawn)时,尝试将父类实例强制转换为子类Pawn,必然触发类型转换异常。 - 未利用多态特性:父类
Piece未定义抽象的valid()方法,导致直接调用时无法自动分发到子类实现,不得不手动做类型转换。
解决步骤
1. 修复棋盘初始化的错误
将棋盘初始化代码中错误的Piece实例替换为对应子类实例,比如把new Piece(1,1,1,0)改成new Pawn(1,1,0)(注意构造方法参数顺序为Color, Rank, File):
// 修改后的第6行 {new Pawn(1,1,0),new Pawn(1,1,1),new Pawn(1,1,2),new Pawn(1,1,3),new Pawn(1,1,4),new Pawn(1,1,5),new Pawn(1,1,6),new Pawn(1,1,7)},
2. 利用多态简化逻辑(推荐)
不需要switch和类型转换,通过多态直接调用子类的valid()方法:
- 把父类
Piece的valid()定义为抽象方法:
public abstract class Piece { // 现有属性和方法(比如getType()) public abstract boolean valid(); // 根据实际需求调整返回值类型 }
- 每个棋子子类重写
valid()方法,实现各自的移动校验逻辑:
public class Pawn extends Piece { // 构造方法 public Pawn(int color, int rank, int file) { super(color, rank, file); // 假设父类有对应构造 } @Override public boolean valid() { // 实现兵的移动合法性校验逻辑 return true; } }
- 在
Board类中直接调用,无需类型转换:
Piece piece = board[7-rank][file]; if (piece != null) { return piece.valid(); } return false;
3. 临时修复(不推荐,但符合“不想重构棋盘”的需求)
如果暂时不想修改Piece类为抽象类,只需确保棋盘内所有棋子都是子类实例,没有直接的Piece实例,即可避免类型转换异常。但这种方式仍需维护switch逻辑,冗余度较高。
内容的提问来源于stack exchange,提问作者NcsAsp
相关产品推荐
相关产品推荐

