
本文介绍如何利用面向对象的继承机制,通过定义抽象基类提取共用字段与方法,使同一函数能安全、简洁地操作多个结构相似但不完全相同的类,避免代码重复。
本文介绍如何利用面向对象的继承机制,通过定义抽象基类提取共用字段与方法,使同一函数能安全、简洁地操作多个结构相似但不完全相同的类,避免代码重复。
在实际开发中,我们常遇到多个类拥有部分相同字段(如 numerator 和 denominator)和对应 getter 方法,但又各自承载不同业务语义(如 A 表示用户分数、B 表示配置项)。若为每个类单独编写逻辑相同的计算函数(如 F()),不仅违反 DRY(Don’t Repeat Yourself)原则,还会增加维护成本与出错风险。
最直接且类型安全的解决方案是提取公共契约,使用抽象基类统一建模。具体做法如下:
- 定义抽象基类:封装共用字段及标准访问接口,声明为 abstract 以防止直接实例化;
- 让具体类继承该基类:A 和 B 分别扩展基类,并保留各自特有的字段(如 firstName、address);
- 函数参数类型上移:将函数 F 的形参类型从具体类改为基类,实现多态调用。
以下是完整可运行的 Java 示例代码:
public abstract class AbstractFraction {
protected Long numerator;
protected Long denominator;
// 构造器(可选)
public AbstractFraction(Long numerator, Long denominator) {
this.numerator = numerator;
this.denominator = denominator;
}
// Getter 方法(必须提供,供 F() 调用)
public Long getNumerator() { return numerator; }
public Long getDenominator() { return denominator; }
// Setter 方法(按需添加)
public void setNumerator(Long numerator) { this.numerator = numerator; }
public void setDenominator(Long denominator) { this.denominator = denominator; }
}
class A extends AbstractFraction {
private String firstName;
public A(Long numerator, Long denominator, String firstName) {
super(numerator, denominator);
this.firstName = firstName;
}
public String getFirstName() { return firstName; }
public void setFirstName(String firstName) { this.firstName = firstName; }
}
class B extends AbstractFraction {
private String address;
public B(Long numerator, Long denominator, String address) {
super(numerator, denominator);
this.address = address;
}
public String getAddress() { return address; }
public void setAddress(String address) { this.address = address; }
}
public class FractionUtil {
/**
* 统一处理任意继承自 AbstractFraction 的子类实例
* @param input 非 null,且 denominator 不应为 0(调用方需保证)
* @return 计算结果,注意 Long 除法会截断,建议转为 double 运算
*/
public static Double F(AbstractFraction input) {
if (input == null || input.getDenominator() == null || input.getDenominator() == 0L) {
throw new IllegalArgumentException("Denominator cannot be null or zero");
}
// 显式转换为 double,避免整数除法丢失精度
return input.getNumerator().doubleValue() / input.getDenominator().doubleValue();
}
}✅ 调用示例:
A a = new A(3L, 4L, "Alice"); B b = new B(5L, 2L, "123 Main St"); System.out.println(FractionUtil.F(a)); // 输出: 0.75 System.out.println(FractionUtil.F(b)); // 输出: 2.5
⚠️ 注意事项:
- 若 A 和 B 已存在继承关系(如均继承自第三方类),则不可再用 extends,此时应改用接口 + 默认方法或组合 + 策略模式;
- getNumerator()/getDenominator() 返回 Long,直接 / 运算仍为长整型除法(Java 中 Long / Long → Long),务必显式转为 double 以获得浮点结果;
- 生产环境中建议增加空值与零分母校验,如示例中所示;
- 抽象基类应聚焦“数学分数语义”,避免混入 UI 或持久化等无关职责,保持单一性。
该方案兼顾类型安全、可读性与可扩展性——未来新增类 C 只需继承 AbstractFraction,即可无缝接入现有函数体系。