从真实开发任务看懂重载与重写

从真实开发任务看懂重载与重写

很多初学者学 "重载""重写" 时,总觉得是枯燥的语法规则,到了实际开发中踩了坑后发现:这两种技术根本不是 "语法糖",而是解决业务痛点的实用工具。接下来我们从电商项目的两个真实开发任务入手,看重载与重写如何从业务需求里自然 "长" 出来。

场景一:商品库存查询工具类 ------ 为什么需要 "重载"?

周二下午的需求评审会,产品经理张姐把后端开发小李叫到工位:"运营同事需要一个库存查询工具,得支持三种场景:一是输个商品 ID 查单条库存;二是选分类 ID + 最小库存阈值,查该分类下库存充足的商品;三是按商品状态(在售 / 下架)查对应库存列表。这周得交付,后续运营要高频用。"

最初的 "糟糕方案":方法名混乱成灾

小李没多想,按 "一个需求一个方法" 的思路写了代码:

kotlin 复制代码
// 商品库存查询工具类(初始版本)
public class StockQueryUtil {
    // 1. 根据商品ID查单个库存
    public Stock getStockByProductId(Long productId) {
        // 查数据库:select * from stock where product_id = ?
        return stock;
    }
    // 2. 根据分类ID+最小库存查列表
    public List<Stock> getStockByCategoryAndMin(Long categoryId, Integer minStock) {
        // 查数据库:select * from stock where category_id = ? and stock_num >= ?
        return stockList;
    }
    // 3. 根据商品状态查列表
    public List<Stock> getStockByStatus(Integer status) {
        // 查数据库:select * from stock where product_status = ?
        return stockList;
    }
}

结果第二天,前端开发小王就来找小李:"你这方法名也太绕了吧?查单个库存是 getStockByProductId 还是 getStockById?查分类的是 getStockListByCategory 还是 getStockByCategoryAndMin?我每次调用都要翻你文档,太影响效率了!"

小李自己也意识到问题:这三个功能本质都是 "查库存",只是查询维度不同,却要记三个长得像又不一样的方法名 ------ 不仅调用方麻烦,后续自己维护时,也容易忘了哪个方法对应哪个场景。

优化方案:用 "重载" 统一方法名

这时小李突然想起大学学过的 "重载":同一类中,方法名相同但参数不同,就能实现 "同一功能,多场景适配"。他立刻重构了代码:

kotlin 复制代码
// 商品库存查询工具类(重载优化版)
public class StockQueryUtil {
    // 1. 根据商品ID查单个库存(参数:商品ID)
    public Stock getStock(Long productId) {
        // 查数据库逻辑不变
        return stock;
    }
    // 2. 根据分类ID+最小库存查列表(参数:分类ID+最小库存)
    public List<Stock> getStock(Long categoryId, Integer minStock) {
        // 查数据库逻辑不变
        return stockList;
    }
    // 3. 根据商品状态查列表(参数:商品状态)
    public List<Stock> getStock(Integer status) {
        // 查数据库逻辑不变
        return stockList;
    }
}

重构后,小王调用时直接写getStock(123L)(查单个)、getStock(45L, 100)(查分类 + 阈值)、getStock(1)(查在售状态)------ 不用记复杂后缀,看参数就知道对应哪个场景,效率直接提上来了。

从场景里 "长" 出重载概念

其实这就是重载的核心:同一类中,为 "语义相同的功能" 定义统一方法名,靠 "参数列表差异"(参数个数、类型、顺序不同)区分不同实现。比如 "查库存" 这个语义,不管是按 ID、按分类 + 阈值,还是按状态,都用getStock(),让代码更贴合 "人类理解习惯"。

这里要注意两个关键规则:

  1. 重载的判断标准只有 "方法名相同 + 参数列表不同",返回值、访问权限不影响(比如上面方法返回Stock或List都可以);
  1. 参数列表差异必须是 "实质性的"(比如getStock(Long)和getStock(Integer)算不同,因为参数类型不同;但getStock(Long)和getStock(Long id)不算,因为只是参数名不同)。

场景二:商品库存扣减接口 ------ 为什么需要 "重写"?

过了一周,张姐又找小李:"现在商品要分'实体商品'和'电子商品'了!实体商品扣库存要关联仓库(比如减货架数量),电子商品扣库存要生成兑换码(比如游戏点卡),但订单系统调用扣库存时,不想管是实体还是电子 ------ 他们只传商品 ID 和数量,剩下的逻辑得咱们封装好。而且下个月要加'预售商品',到时候别让订单系统改代码!"

最初的 "糟糕方案":if-else 堆成山

小李一开始想:简单,加个 "商品类型" 参数,用 if-else 判断不就行了?于是写了这样的代码:

arduino 复制代码
// 库存扣减服务(初始版本)
public class StockDeductService {
    public boolean deductStock(Long productId, int quantity, String productType) {
        if ("PHYSICAL".equals(productType)) {
            // 实体商品逻辑:减仓库库存,生成出库记录
            updateWarehouseStock(productId, quantity);
            createOutboundRecord(productId, quantity);
        } else if ("ELECTRONIC".equals(productType)) {
            // 电子商品逻辑:减虚拟库存,生成兑换码
            updateVirtualStock(productId, quantity);
            generateRedeemCode(productId, quantity);
        } else {
            throw new IllegalArgumentException("未知商品类型");
        }
        return true;
    }
}

但写完他自己就慌了:下个月加 "预售商品",要再加个else if ("PRE_SALE".equals(productType));以后再加 "二手商品",又要加 ------ 这代码会越来越臃肿,而且每次加新类型,都要改deductStock()方法,违反了 "对扩展开放、对修改关闭" 的开发原则(开闭原则)。

优化方案:用 "重写" 实现多态适配

这时小李想到 "面向对象" 的核心思想:父类定义规则,子类实现细节。他重构了代码,引入 "重写":

java 复制代码
// 1. 父类:定义统一的"扣库存"规则(抽象方法)
public abstract class Product {
    // 抽象方法:只定义"扣库存"的入参和返回值,不写具体逻辑
    public abstract boolean deductStock(int quantity);
}
// 2. 实体商品子类:重写父类方法,实现实体商品逻辑
public class PhysicalProduct extends Product {
    private Long warehouseId; // 实体商品特有属性:仓库ID
    @Override
    public boolean deductStock(int quantity) {
        // 实体商品专属逻辑:减仓库库存+生成出库记录
        updateWarehouseStock(warehouseId, quantity);
        createOutboundRecord(getProductId(), quantity);
        return true;
    }
}
// 3. 电子商品子类:重写父类方法,实现电子商品逻辑
public class ElectronicProduct extends Product {
    @Override
    public boolean deductStock(int quantity) {
        // 电子商品专属逻辑:减虚拟库存+生成兑换码
        updateVirtualStock(getProductId(), quantity);
        generateRedeemCode(getProductId(), quantity);
        return true;
    }
}
// 4. 调用方(订单系统):只认父类,不管子类
public class OrderService {
    // 商品工厂:根据商品ID获取对应子类对象(隐藏子类创建细节)
    private Product getProduct(Long productId) {
        String type = getProductTypeFromDb(productId); // 查数据库获取商品类型
        if ("PHYSICAL".equals(type)) {
            return new PhysicalProduct(productId, 1001L); // 1001是仓库ID
        } else {
            return new ElectronicProduct(productId);
        }
    }
    // 订单处理:调用扣库存时,只操作父类引用
    public void processOrder(Long productId, int quantity) {
        Product product = getProduct(productId);
        // 关键:不用判断商品类型,直接调用父类方法,自动执行子类逻辑
        product.deductStock(quantity);
    }
}

这样一来,订单系统调用deductStock()时,完全不用关心商品是实体还是电子 ------ 父类引用Product指向哪个子类对象,就自动执行哪个子类的deductStock()方法(这就是多态的落地)。

下个月加 "预售商品" 时,只需要新增一个PreSaleProduct子类,重写deductStock()方法,订单系统的代码一行都不用改:

scala 复制代码
// 新增预售商品子类:无需修改原有代码
public class PreSaleProduct extends Product {
    @Override
    public boolean deductStock(int quantity) {
        // 预售商品专属逻辑:减预售库存,更新发货时间
        updatePreSaleStock(getProductId(), quantity);
        updateDeliveryTime(getProductId());
        return true;
    }
}

从场景里 "长" 出重写概念

这就是重写的核心:父子类间,子类对父类的 "抽象方法 / 普通方法" 进行 "同名、同参数、同返回值" 的具体实现,目的是让 "同一行为在不同子类中有不同表现",最终支撑多态。

重写有三个关键规则:

  1. 方法签名必须完全一致(方法名、参数列表、返回值类型完全相同,子类返回值可是父类的子类,比如父类返回Product,子类可返回PhysicalProduct);
  1. 子类方法的访问权限不能低于父类(比如父类是public,子类不能是private);
  1. 父类如果是抽象方法,子类必须重写(除非子类也是抽象类)。

重载与重写:一张表分清核心区别

看到这里,可能有人会混淆 "重载" 和 "重写"------ 其实结合前面的场景,用一张表就能分清:

对比维度 重载(Overload) 重写(Override)
适用场景 同一类中,"同一功能多场景" 父子类间,"同一行为多实现"
方法签名 方法名相同,参数列表不同 方法名、参数列表、返回值完全相同
核心目的 简化调用(不用记多个方法名) 实现多态(提高代码扩展性)
调用时的区别 编译期确定调用哪个方法(静态绑定) 运行期确定调用哪个方法(动态绑定)
示例对应 getStock(Long)/getStock(Integer) PhysicalProduct.deductStock()/ElectronicProduct.deductStock()

最后:技术永远服务于业务

很多人学编程时,会陷入 "死记语法规则" 的误区,但从上面的真实场景能看出:重载不是 "为了同名而同名",而是为了解决 "方法名混乱" 的业务痛点;重写也不是 "为了继承而重写",而是为了解决 "代码扩展性差" 的业务难题。

记住:开发的本质是用技术解决业务问题。当你下次纠结 "要不要用重载 / 重写" 时,不妨回到业务场景里想一想:这样写能不能让调用更简单?能不能让后续加需求时少改代码?想清楚这两个问题,技术选型自然就清晰了。

相关推荐
华仔啊7 分钟前
别再问了!Java里这几种场景,用抽象类就对了
java·后端
guojl11 分钟前
Gateway源码分析
后端·微服务
tingting011926 分钟前
Spring Boot 外部配置指定不生效的原因与解决
java·spring boot·后端
2501_9096867037 分钟前
基于SpringBoot的网上点餐系统
java·spring boot·后端
天天摸鱼的java工程师43 分钟前
聊聊线程池中哪几种状态,分别表示什么?8 年 Java 开发:从业务踩坑到源码拆解(附监控实战)
java·后端
杨杨杨大侠1 小时前
第4篇:AOP切面编程 - 无侵入式日志拦截
java·后端·开源
IT_陈寒2 小时前
Python 3.12 新特性实战:5个让你的代码效率提升50%的技巧!🔥
前端·人工智能·后端
Apifox2 小时前
Apifox 8 月更新|新增测试用例、支持自定义请求示例代码、提升导入/导出 OpenAPI/Swagger 数据的兼容性
前端·后端·测试