多态应用场景
写代码三年,你可能每天都在用多态,只是没意识到。
你写的 button.setOnClick(...),是它。你调用的 order.checkout(100),也是它。集合遍历时的多态就更不用说了。这些场景散落在真实项目里,但很少有人把它们串起来讲。
这一节,把它们放在一起。
回调机制
GUI 开发里最常见的需求:点击按钮,做点事情。但按钮怎么知道你要做什么?
一个朴素的想法是把逻辑写死在按钮里:
// 按钮内部写死逻辑 → 换个功能就要改按钮代码
class Button {
void click() {
// 永远只有这一种行为
System.out.println("Clicked!");
}
}显然不行。按钮应该只负责「什么时候触发」,具体「触发后做什么」,由外部决定。这就是回调的本质:把行为当作参数传进去。
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,每次加新渠道都要改核心代码。
换个思路:把每种支付方式抽象成一个「策略」,用的时候选一个。
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 的对象。支付方式从代码里消失了,变成了可注入的配置。
集合遍历
再往前走一步。假设有一群动物:
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("喵~");
}
}现在有个需求:让它们各自叫一声。
不用多态,你得这样写:
Dog dog = new Dog();
Cat cat = new Cat();
dog.speak();
cat.speak();每加一种动物,就要加一行调用。这不是代码,这是体力活。
用多态,一个循环搞定:
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
这个「不知道」不是缺陷,是设计。多态把「变化的部分」封装成一个可替换的东西,让稳定的那部分代码不用动。
下一步
多态能正常工作,背后有一个机制在撑着:动态绑定——方法到底调用的是父类还是子类,不是编译时拍板,而是运行时决定的。
下一节,聊聊这个。
