Skip to content

多态应用场景

写代码三年,你可能每天都在用多态,只是没意识到。

你写的 button.setOnClick(...),是它。你调用的 order.checkout(100),也是它。集合遍历时的多态就更不用说了。这些场景散落在真实项目里,但很少有人把它们串起来讲。

这一节,把它们放在一起。

回调机制

GUI 开发里最常见的需求:点击按钮,做点事情。但按钮怎么知道你要做什么?

一个朴素的想法是把逻辑写死在按钮里:

java
// 按钮内部写死逻辑 → 换个功能就要改按钮代码
class Button {
    void click() {
        // 永远只有这一种行为
        System.out.println("Clicked!");
    }
}

显然不行。按钮应该只负责「什么时候触发」,具体「触发后做什么」,由外部决定。这就是回调的本质:把行为当作参数传进去

java
public class CallbackDemo {

    interface ClickHandler {
        void onClick();
    }

    static class Button {
        private ClickHandler handler;

        void setOnClick(ClickHandler handler) {
            this.handler = handler;
        }

        void click() {
            if (handler != null) {
                handler.onClick();
            }
        }
    }

    public static void main(String[] args) {
        Button button = new Button();

        // 登录按钮:点完弹出提示
        button.setOnClick(() -> System.out.println("登录成功!"));
        button.click();

        // 换成提交按钮:点完提交表单
        button.setOnClick(() -> System.out.println("表单已提交"));
        button.click();
    }
}

Button 不知道也不会关心 handler 具体是什么。它只负责一件事:触发时调用 handler.onClick()。这是多态的起点——同一行代码,不同的行为由传入的对象决定

策略模式

光有回调还不够。有些场景下,你需要更系统的设计。

电商系统里,支付是个典型问题:微信支付、支付宝、银行卡……每种支付方式逻辑不同,而且可能随时增减。如果把支付逻辑写成 if-else,每次加新渠道都要改核心代码。

换个思路:把每种支付方式抽象成一个「策略」,用的时候选一个。

java
public class StrategyDemo {

    interface PaymentStrategy {
        void pay(double amount);
    }

    static class AlipayStrategy implements PaymentStrategy {
        @Override
        public void pay(double amount) {
            System.out.println("支付宝收款:" + amount + " 元");
        }
    }

    static class WechatPayStrategy implements PaymentStrategy {
        @Override
        public void pay(double amount) {
            System.out.println("微信支付收款:" + amount + " 元");
        }
    }

    // 订单只依赖策略接口,不关心具体实现
    static class Order {
        private PaymentStrategy strategy;

        void setPaymentStrategy(PaymentStrategy strategy) {
            this.strategy = strategy;
        }

        void checkout(double amount) {
            strategy.pay(amount);
        }
    }

    public static void main(String[] args) {
        Order order = new Order();

        // 用户选择支付宝
        order.setPaymentStrategy(new AlipayStrategy());
        order.checkout(299.00);

        // 用户换成微信支付 → 换策略对象即可
        order.setPaymentStrategy(new WechatPayStrategy());
        order.checkout(199.00);
    }
}

关键在于:Order 不需要知道「怎么付钱」,它只需要一个实现了 PaymentStrategy 的对象。支付方式从代码里消失了,变成了可注入的配置。

集合遍历

再往前走一步。假设有一群动物:

java
static class Animal {
    void speak() {
        System.out.println("...");
    }
}

static class Dog extends Animal {
    @Override
    void speak() {
        System.out.println("汪汪!");
    }
}

static class Cat extends Animal {
    @Override
    void speak() {
        System.out.println("喵~");
    }
}

现在有个需求:让它们各自叫一声。

不用多态,你得这样写:

java
Dog dog = new Dog();
Cat cat = new Cat();
dog.speak();
cat.speak();

每加一种动物,就要加一行调用。这不是代码,这是体力活。

用多态,一个循环搞定:

java
public class CollectionPolyDemo {

    static class Animal {
        void speak() {
            System.out.println("...");
        }
    }

    static class Dog extends Animal {
        @Override
        void speak() {
            System.out.println("汪汪!");
        }
    }

    static class Cat extends Animal {
        @Override
        void speak() {
            System.out.println("喵~");
        }
    }

    public static void main(String[] args) {
        List<Animal> animals = Arrays.asList(new Dog(), new Cat());

        // 同一个循环,同一个 speak(),不同的结果
        for (Animal animal : animals) {
            animal.speak();
        }
    }
}

animal.speak() 运行时到底调用哪个方法,由循环里实际的对象决定。这就是集合多态的价值:处理一组不同类型的对象,用同一套代码,不需要知道它们具体是谁

这三个场景的共同点

回调、策略、集合遍历,表面上是三个不同的东西,内核是一样的:

调用方只关心「做什么」,不关心「谁来做」和「怎么做」。

  • Button 不知道也不想知道点击后干什么
  • Order 不知道也不想知道用什么方式付款
  • for 循环不知道也不知道它遍历的是 Dog 还是 Cat

这个「不知道」不是缺陷,是设计。多态把「变化的部分」封装成一个可替换的东西,让稳定的那部分代码不用动。

下一步

多态能正常工作,背后有一个机制在撑着:动态绑定——方法到底调用的是父类还是子类,不是编译时拍板,而是运行时决定的。

下一节,聊聊这个。

基于 VitePress 构建