接口与多态
接口与多态
目标:掌握多态的三条件与动态绑定;理解接口的本质是「制定标准/契约」;会声明
interface、用implements实现(含多接口);会用接口变量做回调、用接口类型作方法参数——这正是接口实现多态的方式;能辨析接口与抽象类的取舍。
一、多态:同一消息,不同行为
多态(Polymorphism) = 「多种形态」:同一条消息(方法调用),发给不同对象,产生不同的行为。
Animal a = new Dog(); // 向上转型:父类引用指向子类对象
a.speak(); // 运行期执行 Dog 的 speak(),而不是 Animal 的
- 子类对象可以被视为父类对象(
Animal a = new Dog();)——只能访问父类声明的成员; - 父类对象不能被当作某个具体子类的对象(向下强转需显式、可能抛
ClassCastException); - 上转型后调用被重写的方法,执行的是实际对象的版本——这就是动态绑定。
动手:看多态分派
交互实验:多态与动态绑定(PolymorphismDemo)
选一个「引用 = 对象」场景,点调用按钮:编译期看引用类型有没有这个方法,运行期按实际对象类型执行对应版本。
// 点按钮执行调用
speak() 才能产生不同行为。多态的三个条件
- 继承(或实现):子类与父类(实现类与接口)存在 is-a 关系;
- 方法重写:子类重写了父类的同名同参方法;
- 向上转型:用父类/接口类型的引用指向子类对象。
注意:重载(Overload)与多态分派无关——重载是同一个类里同名不同参,编译期就确定了调哪个。
二、接口 = 制定标准
万能充电器、主板的 USB/HDMI 口、墙上的插座——它们的共同点:制定统一标准,让「使用者」与「被使用者」按标准对接,彼此不必关心对方内部实现。
Java 的 interface 正是如此:调用方只依赖接口(标准),不依赖具体类——解耦、可替换、可扩展。
三、声明与实现
public interface IUsb {
void connect(); // 默认 public abstract
void disconnect();
}
public class Phone implements IUsb {
public void connect() { System.out.println("手机通过USB连接"); }
public void disconnect() { System.out.println("手机断开USB"); }
}
规则要点:
- 接口方法默认
public abstract(只有声明、没有实现);接口变量默认public static final(只能是常量); - 类用
implements实现接口,可以同时实现多个(多继承效果);接口也可以继承多个接口; - 非抽象类实现接口必须把所有抽象方法具体化;
- ⚠️ 高频错误:接口方法默认
public,实现类里也必须写public——漏写会被视为「缩小访问权限」而编译报错。
四、用接口实现多态:回调与接口参数
继承是多态的一条路,接口是另一条——不相关的类只要实现了同一接口,就能被统一对待:
IUsb dev = new Phone(); // 接口变量引用实现类对象(向上转型到接口)
dev.connect(); // 实际执行 Phone 的 connect()
void use(IUsb dev) { // 形参是接口类型:手机、U盘、相机都能传入
dev.connect();
}
- 接口回调:实现类对象的引用赋给接口变量,经接口变量调方法——与「父类引用指向子类对象」同理;
- 接口作参数:任何实现该接口的对象都可传入,方法内部按实际对象动态绑定;
- 对比继承版多态:
Animal a = new Dog()要求 is-a 血缘;IUsb dev = new Phone()只要求「实现了标准」——手机和宠物这类毫无血缘的类也能共享同一套调用代码。
交互实验:回调注册器(CallbackRegistry)
接口 IUsb { connect(); } 有三个实现类。 把监听器注册到某个事件下,再触发该事件——每个已注册的接口变量都会调用其实际对象的 connect()(动态分派)。
① 事件
② 注册监听器(点选)
③ 触发事件
触发后这里显示调用序列。
关键:方法里拿到的形参是接口类型,运行时按实际对象决定调哪个实现——手机、U盘、相机毫无血缘,却能共享同一套调用代码。
五、接口 vs 抽象类
交互实验:接口 vs 抽象类(InterfaceVsAbstract)
点任意一行展开说明。
| 接口 interface | 抽象类 abstract class | |
|---|---|---|
| 方法 | 全部为抽象方法(Java 8+ 可有 default / static 方法) | 可含抽象方法,也可含普通(已实现)方法 |
| 继承 / 实现 | 一个类可以实现多个接口(多继承效果);接口也可继承多个接口 | 一个类只能继承一个抽象类(单继承) |
| 成员变量 | 默认 public static final —— 只能是常量 | 普通成员变量(各种访问修饰符) |
| 与类的关系 | 与实现类不构成继承体系;不相关的类可实现同一接口 | 属于类的继承体系(is-a) |
| 构造方法 | 没有 | 有(子类 new 时经 super() 调用) |
只有必须共享已实现的成员/方法时才考虑抽象类;否则优先用接口——更低耦合、更好扩展。
思考题
- 写出多态的三个条件,并说明为什么「重载」不算在内。
- 用「万能充电器 / 主板接口」类比,说明接口「制定标准」的作用。
Animal a = new Dog()与IUsb dev = new Phone()两种多态写法各适用于什么场景?
学习小测验
学习小测验
Animal a = new Dog(); 后调用 a.speak()(Dog 已重写),执行的是?
实现多态的必要条件不包括( )
Java 中用于定义接口的关键字是( )
接口中的方法默认是( )的
类实现多个接口的写法是( )
实现接口时漏写 public 会怎样?
IUsb dev = new Phone(); 后 dev.connect() 执行的是?
关于接口与抽象类,正确的是( )