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

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

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

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

周二下午的需求评审会,产品经理张姐把后端开发小李叫到工位:"运营同事需要一个库存查询工具,得支持三种场景:一是输个商品 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()

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

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

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

相关推荐
元亓亓亓6 小时前
SSM--day4--SpringMVC(补充)
java·后端·ssm
沐雨橙风ιε6 小时前
Spring Boot整合Apache Shiro权限认证框架(应用篇)
java·spring boot·后端·apache shiro
考虑考虑6 小时前
fastjson调用is方法开头注意
java·后端·java ee
小蒜学长7 小时前
springboot基于javaweb的小零食销售系统的设计与实现(代码+数据库+LW)
java·开发语言·数据库·spring boot·后端
brzhang7 小时前
为什么 OpenAI 不让 LLM 生成 UI?深度解析 OpenAI Apps SDK 背后的新一代交互范式
前端·后端·架构
EnCi Zheng7 小时前
JPA 连接 PostgreSQL 数据库完全指南
java·数据库·spring boot·后端·postgresql
brzhang7 小时前
OpenAI Apps SDK ,一个好的 App,不是让用户知道它该怎么用,而是让用户自然地知道自己在做什么。
前端·后端·架构
LucianaiB8 小时前
从玩具到工业:基于 CodeBuddy code CLI 构建电力变压器绕组短路智能诊断系统
后端
武子康9 小时前
大数据-118 - Flink 批处理 DataSet API 全面解析:应用场景、代码示例与优化机制
大数据·后端·flink
不要再敲了9 小时前
Spring Security 完整使用指南
java·后端·spring