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

如何避免将this作为类参数传入?最佳实践咨询

避免在构造器中传递this的最佳实践

你说得太对了——在构造器里把this传递给其他对象确实是个风险点,因为此时当前类的初始化还没完成,其他对象如果立刻调用这个未完全初始化的实例的方法或访问属性,很容易触发不可预期的错误,甚至空指针异常。针对你给出的Game和Board的场景,我整理了几个常用的最佳实践:


1. 延迟依赖注入(初始化后再设置依赖)

把Board的初始化和Game实例的传递分开,先完成Game的所有字段初始化,再通过 setter 方法把Game实例传给Board。这样能确保Game处于完全可用的状态,不会出现半初始化的问题。

示例代码:

public class Game {
    private Player p1, p2, currentPlayer;
    private Board board;

    Game() {
        board = new Board(); // 先初始化Board,不传递this
        // 先完成Game自身字段的初始化
        p1 = new Player();
        p2 = new Player();
        currentPlayer = p1;
        // 此时Game已完全初始化,再设置依赖
        board.setGame(this);
    }

    private boolean hasFieldsToBeClicked() {
        return board.checkFieldsToBeClicked();
    }
}

public class Board {
    private Game game;

    public void setGame(Game game) {
        this.game = game;
        // 这里可以执行需要依赖Game的初始化逻辑
    }

    public boolean checkFieldsToBeClicked() {
        // 业务逻辑实现
        return true;
    }
}

2. 使用静态工厂方法代替构造器

静态工厂方法能让我们更灵活地控制对象的初始化流程:先完成Game的所有初始化步骤,再创建Board并传递完整的Game实例。这种方式还能让代码的意图更清晰,避免外部直接调用构造器带来的风险。

示例代码:

public class Game {
    private Player p1, p2, currentPlayer;
    private Board board;

    // 私有构造器,强制外部使用工厂方法创建实例
    private Game() {
        // 先初始化Game的所有字段
        p1 = new Player();
        p2 = new Player();
        currentPlayer = p1;
    }

    // 静态工厂方法,封装完整的初始化流程
    public static Game createGame() {
        Game game = new Game();
        // 此时Game已完全初始化,再创建Board并传递this
        game.board = new Board(game);
        return game;
    }

    private boolean hasFieldsToBeClicked() {
        return board.checkFieldsToBeClicked();
    }
}

3. 重构依赖关系,避免Board直接持有Game引用

如果业务逻辑允许,可以调整类之间的依赖方向,或者提取公共逻辑到第三方类,让Board不需要直接持有Game的引用。比如把Board需要的游戏状态数据单独封装,作为参数传递给Board的方法,而不是让Board依赖整个Game实例。

示例代码:

public class Game {
    private Player p1, p2, currentPlayer;
    private Board board;

    Game() {
        board = new Board();
        p1 = new Player();
        p2 = new Player();
        currentPlayer = p1;
    }

    private boolean hasFieldsToBeClicked() {
        // 把Board需要的信息作为参数传递,而非让Board持有Game引用
        return board.checkFieldsToBeClicked(new GameState(p1, p2, currentPlayer));
    }
}

public class Board {
    public boolean checkFieldsToBeClicked(GameState gameState) {
        // 使用传入的游戏状态数据执行逻辑判断
        return true;
    }
}

// 提取的游戏状态类,封装Board需要的所有数据
class GameState {
    private Player p1;
    private Player p2;
    private Player currentPlayer;

    public GameState(Player p1, Player p2, Player currentPlayer) {
        this.p1 = p1;
        this.p2 = p2;
        this.currentPlayer = currentPlayer;
    }

    // 提供必要的getter方法
}

4. 禁止Board在构造器中执行依赖Game的逻辑

如果必须在构造器中传递this,那一定要确保Board的构造器中不直接调用Game的任何方法或访问其字段,把依赖Game的逻辑延迟到专门的初始化方法中,等Game完全初始化后再调用这个方法。

示例代码:

public class Game {
    private Player p1, p2, currentPlayer;
    private Board board;

    Game() {
        board = new Board(this);
        // 先完成Game自身的所有初始化
        p1 = new Player();
        p2 = new Player();
        currentPlayer = p1;
        // 通知Board可以执行依赖Game的初始化逻辑了
        board.init();
    }

    private boolean hasFieldsToBeClicked() {
        return board.checkFieldsToBeClicked();
    }
}

public class Board {
    private Game game;

    public Board(Game game) {
        this.game = game;
        // 构造器中只保存引用,不做任何依赖Game的操作
    }

    public void init() {
        // 这里才执行需要使用Game实例的初始化逻辑
    }

    public boolean checkFieldsToBeClicked() {
        // 业务逻辑实现
        return true;
    }
}

内容的提问来源于stack exchange,提问作者Fabian Zbinden

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 09:20:17