You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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)

问题根源

  1. 棋盘初始化错误:在第6行(白方兵行)的第一个元素是new Piece(1,1,1,0),这是父类Piece的实例,而非Pawn子类实例。当getType()返回1(代表Pawn)时,尝试将父类实例强制转换为子类Pawn,必然触发类型转换异常。
  2. 未利用多态特性:父类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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.26 10:42:10