Java 中的奇异递归模板模式:独特地利用多态性
大约 3 分钟
也称为
- CRTP
- 混合继承
- 递归类型绑定
- 递归泛型
- 静态多态性
奇异递归模板模式的意图
奇异递归模板模式 (CRTP) 是一种强大的 Java 设计模式,用于实现静态多态性。通过使一个类模板继承自自身类的模板实例化,CRTP 使得方法重写和编译时多态行为成为可能,从而提高了 Java 应用程序的效率和性能。
奇异递归模板模式的详细解释以及实际示例
实际示例
假设一个图书馆系统管理着各种类型的媒体:书籍、DVD 和杂志。每种媒体类型都有其特定的属性和行为,但它们都共享一些通用的功能,例如借阅和归还。通过在 Java 中应用奇异递归模板模式 (CRTP),您可以创建一个包含这些通用方法的基类模板
MediaItem
。每种特定媒体类型(例如,Book
、DVD
、Magazine
)都会使用自身作为模板参数从MediaItem
继承。这种方法允许每种媒体类型有效地定制共享功能,避免了虚拟方法带来的开销。
通俗地说
Java 中的 CRTP 确保类型中的某些方法可以接受其子类型的特定参数,从而在编译时实现更高效、类型安全的泛型行为。
维基百科说
奇异递归模板模式 (CRTP) 是一种惯用语,最初用于 C++,其中类 X 继承自使用 X 本身作为模板参数的类模板实例化。
CRTP 在 Java 中的编程示例
对于计划举办比赛的综合格斗推广活动来说,确保比赛在相同体重级别的运动员之间进行至关重要。这避免了体型差异很大的格斗者之间的不公平对抗,例如重量级选手与雏量级选手对决。
让我们定义泛型接口 Fighter
。
public interface Fighter<T> {
void fight(T t);
}
MMAFighter
类用于实例化由体重级别区分的格斗者。
@Slf4j
@Data
public class MmaFighter<T extends MmaFighter<T>> implements Fighter<T> {
private final String name;
private final String surname;
private final String nickName;
private final String speciality;
@Override
public void fight(T opponent) {
LOGGER.info("{} is going to fight against {}", this, opponent);
}
}
以下是 MMAFighter
的一些子类型。
class MmaBantamweightFighter extends MmaFighter<MmaBantamweightFighter> {
public MmaBantamweightFighter(String name, String surname, String nickName, String speciality) {
super(name, surname, nickName, speciality);
}
}
public class MmaHeavyweightFighter extends MmaFighter<MmaHeavyweightFighter> {
public MmaHeavyweightFighter(String name, String surname, String nickName, String speciality) {
super(name, surname, nickName, speciality);
}
}
允许格斗者与相同体重级别的对手比赛。如果对手的体重级别不同,则会引发错误。
public static void main(String[] args) {
MmaBantamweightFighter fighter1 = new MmaBantamweightFighter("Joe", "Johnson", "The Geek", "Muay Thai");
MmaBantamweightFighter fighter2 = new MmaBantamweightFighter("Ed", "Edwards", "The Problem Solver", "Judo");
fighter1.fight(fighter2);
MmaHeavyweightFighter fighter3 = new MmaHeavyweightFighter("Dave", "Davidson", "The Bug Smasher", "Kickboxing");
MmaHeavyweightFighter fighter4 = new MmaHeavyweightFighter("Jack", "Jackson", "The Pragmatic", "Brazilian Jiu-Jitsu");
fighter3.fight(fighter4);
}
程序输出
08:42:34.048 [main] INFO crtp.MmaFighter -- MmaFighter(name=Joe, surname=Johnson, nickName=The Geek, speciality=Muay Thai) is going to fight against MmaFighter(name=Ed, surname=Edwards, nickName=The Problem Solver, speciality=Judo)
08:42:34.054 [main] INFO crtp.MmaFighter -- MmaFighter(name=Dave, surname=Davidson, nickName=The Bug Smasher, speciality=Kickboxing) is going to fight against MmaFighter(name=Jack, surname=Jackson, nickName=The Pragmatic, speciality=Brazilian Jiu-Jitsu)
何时在 Java 中使用奇异递归模板模式
- 当您需要通过继承来扩展类的功能,但出于效率原因更倾向于编译时多态性而不是运行时多态性时。
- 当您希望避免虚拟函数的开销,但仍希望实现多态行为时。
- 在模板元编程中,提供可以在编译时选择的功能或策略的实现。
- 当您在对象层次结构中链接方法时出现类型冲突。
- 您希望使用一个参数化类方法,该方法可以接受类的子类作为参数,从而允许将其应用于从该类继承的对象。
- 您希望某些方法仅对相同类型的实例起作用,例如,为了实现相互可比性。
奇异递归模板模式 Java 教程
奇异递归模板模式在 Java 中的实际应用
- 在模板库中实现编译时多态接口。
- 在性能至关重要的库中提高代码重用,例如,在数学计算、嵌入式系统和实时处理应用程序中。
- 在各种 Java 库中实现
Cloneable
接口。
奇异递归模板模式的优缺点
优点
- 消除了虚拟函数调用的开销,提高了性能。
- 安全地重用基类代码,而不会出现与多重继承相关的风险。
- 在编译时多态性场景中,提供更大的灵活性可扩展性。
权衡
- 由于模板和继承之间的相互作用,理解和调试变得更加复杂。
- 可能会导致代码膨胀,因为模板的每次实例化都会生成一个新的类。
- 与运行时多态性相比,灵活性较低,因为行为必须完全在编译时确定。