如何避免在接口处理中使用instanceof与强制类型转换?
问题描述
希望避免使用instanceof以及将接口强制转换为实现类的操作。现有一个以接口FFF为参数的方法,需根据传入的fff实例是AAA类还是TTT类调用对应私有方法,其中一个私有方法要调用FFF接口某实现类特有的方法。
实体类代码
public interface FFF { Long getShipperId(); default void updatePermissions(final Permissions permissions) { ... this.setPermissions(permissions); } } public class AAA implements FFF { private TTT window; public TTT getWindow(); } public class TTT implements FFF { }
服务类代码
public class MyService { private Service service; // Spring Boot注入 public void updatePermissions(FFF fff) { final SomeClass someVariable; if (fff instanceof TTT) { someVariable = Optional.ofNullable(...) .map(...) .orElseGet(() -> createDefaultSettings(fff.getShipperId())); } else { someVariable= Optional.ofNullable(...) .map(...) .orElseGet(() -> returnPermissions(fff)); } fff.updatePermissions(someVariable); } private Permissions createDefaultSettings(Long shipperId){ final Settings settings= service.getSettings(shipperId); return new Permissions(settings.getDefault(), false); } private Permissions returnPermissions(FFF fff) { AAA aaa= (AAA) fff; return Optional.ofNullable(aaa.getWindow()) // FFF接口没有getWindow()方法 .map(Window::getPermissions) .map(...) .orElseGet(() -> createDefaultSettings(aaa.getShipperId())); } }
疑问
- 如何避免使用
instanceof以及AAA aaa= (AAA) fff;这样的强制转换? - 当前写法是否可行?
- 是否有更优方案?比如是否该在
FFF接口中添加getWindow()方法,但这样TTT类需实现无用方法。
解决方案
当前写法的可行性
当前写法能运行,但存在明显缺陷:
- 新增
FFF实现类时,必须修改updatePermissions的分支判断,违反开闭原则; - 强制类型转换若遇到非
AAA实例(比如后续新增的实现类),会直接抛出ClassCastException,运行风险高; - 代码耦合度高,
MyService需要知晓FFF所有实现类的细节,不利于长期维护。
不推荐在FFF接口添加getWindow()
直接在FFF中新增getWindow()方法虽然省事,但会带来两个问题:
TTT类被迫实现无意义的方法,要么返回null要么抛出异常,违反接口隔离原则;- 接口职责被污染,
FFF原本是通用接口,现在绑定了特定实现类的专属方法,语义混乱。
更优方案
方案1:使用访问者模式
核心是让FFF接口定义接受访问者的方法,由不同实现类自行处理访问逻辑,将权限计算逻辑分散到各个实现类,彻底消除类型判断。
步骤1:定义访问者接口
public interface FFVisitor { Permissions visit(TTT ttt); Permissions visit(AAA aaa); }
步骤2:修改FFF接口,添加accept方法
public interface FFF { Long getShipperId(); default void updatePermissions(final Permissions permissions) { ... this.setPermissions(permissions); } Permissions accept(FFVisitor visitor); }
步骤3:实现类实现accept方法
public class AAA implements FFF { private TTT window; public TTT getWindow() { return window; } @Override public Permissions accept(FFVisitor visitor) { return visitor.visit(this); } } public class TTT implements FFF { @Override public Permissions accept(FFVisitor visitor) { return visitor.visit(this); } }
步骤4:重构MyService
public class MyService { private Service service; public void updatePermissions(FFF fff) { FFVisitor visitor = new FFVisitor() { @Override public Permissions visit(TTT ttt) { return Optional.ofNullable(...) .map(...) .orElseGet(() -> createDefaultSettings(ttt.getShipperId())); } @Override public Permissions visit(AAA aaa) { return Optional.ofNullable(aaa.getWindow()) .map(Window::getPermissions) .map(...) .orElseGet(() -> createDefaultSettings(aaa.getShipperId())); } }; Permissions someVariable = fff.accept(visitor); fff.updatePermissions(someVariable); } private Permissions createDefaultSettings(Long shipperId){ final Settings settings= service.getSettings(shipperId); return new Permissions(settings.getDefault(), false); } }
新增FFF实现类时,只需扩展FFVisitor接口并在新类中实现accept方法即可,完全符合开闭原则。
方案2:拆分功能接口
把AAA特有的getWindow行为抽成独立接口,MyService通过方法重载利用多态处理,降低耦合度。
步骤1:新增WindowHolder接口
public interface WindowHolder { TTT getWindow(); }
步骤2:让AAA实现该接口
public class AAA implements FFF, WindowHolder { private TTT window; @Override public TTT getWindow() { return window; } }
步骤3:重构MyService
public class MyService { private Service service; public void updatePermissions(FFF fff) { Permissions someVariable; if (fff instanceof WindowHolder) { someVariable = calculatePermissions((WindowHolder) fff, fff.getShipperId()); } else { someVariable = calculateDefaultPermissions(fff.getShipperId()); } fff.updatePermissions(someVariable); } private Permissions calculateDefaultPermissions(Long shipperId) { return Optional.ofNullable(...) .map(...) .orElseGet(() -> createDefaultSettings(shipperId)); } private Permissions calculatePermissions(WindowHolder holder, Long shipperId) { return Optional.ofNullable(holder.getWindow()) .map(Window::getPermissions) .map(...) .orElseGet(() -> createDefaultSettings(shipperId)); } private Permissions createDefaultSettings(Long shipperId){ final Settings settings= service.getSettings(shipperId); return new Permissions(settings.getDefault(), false); } }
此方案虽仍用到instanceof,但仅针对功能接口而非具体实现类,后续新增实现类时,只要不需要getWindow能力就无需修改代码,耦合度远低于原写法。
方案3:将权限计算逻辑移至实现类
若权限计算逻辑与实现类强相关,可在FFF接口定义抽象方法,由各个实现类自行实现:
步骤1:修改FFF接口
public interface FFF { Long getShipperId(); default void updatePermissions(final Permissions permissions) { ... this.setPermissions(permissions); } Permissions calculatePermissions(Service service); }
步骤2:实现类各自实现计算逻辑
public class AAA implements FFF { private TTT window; public TTT getWindow() { return window; } @Override public Permissions calculatePermissions(Service service) { return Optional.ofNullable(window) .map(Window::getPermissions) .map(...) .orElseGet(() -> { Settings settings = service.getSettings(getShipperId()); return new Permissions(settings.getDefault(), false); }); } } public class TTT implements FFF { @Override public Permissions calculatePermissions(Service service) { return Optional.ofNullable(...) .map(...) .orElseGet(() -> { Settings settings = service.getSettings(getShipperId()); return new Permissions(settings.getDefault(), false); }); } }
步骤3:简化MyService
public class MyService { private Service service; public void updatePermissions(FFF fff) { Permissions someVariable = fff.calculatePermissions(service); fff.updatePermissions(someVariable); } }
此方案最简洁,完全消除类型判断,将逻辑归位到各自实现类,符合单一职责原则。若实现类需要依赖Service,可通过构造器注入或调用时传入。
内容的提问来源于stack exchange,提问作者Matexon
相关产品推荐
相关产品推荐

