Java interface 接口的巧妙应用:让你的代码更优雅
文章目录
- 接口作为行为契约
- 接口默认方法(Default Methods 常用)
- 接口组合(Interface Segregation)
- 函数式接口与 Lambda 表达式
- 接口驱动的工厂模式
- 接口与依赖注入
- 结语
- 推荐阅读文章
在 Java 开发中,接口是一个非常常见且强大的工具。它不仅仅是一个实现多态的方式,还可以通过灵活的应用提升代码的可读性、扩展性和维护性。本文将从几个不同的角度出发,探讨 Java 接口的巧妙应用,帮助你更好地利用它们来写出优雅、灵活的代码。
接口作为行为契约
接口的最基本用法是作为行为契约。它定义了一组方法,而不关心这些方法如何实现。这样做可以解耦代码的定义和实现,让程序更加灵活。
场景:支付系统中的支付方式
public interface PaymentMethod {void pay(double amount);
}public class CreditCardPayment implements PaymentMethod {@Overridepublic void pay(double amount) {System.out.println("Paid " + amount + " with Credit Card");}
}public class PayPalPayment implements PaymentMethod {@Overridepublic void pay(double amount) {System.out.println("Paid " + amount + " with PayPal");}
}
这样一来,无论是使用信用卡、PayPal 还是其他支付方式,代码都可以通过接口处理,不必关注具体实现。
接口默认方法(Default Methods 常用)
Java 8 引入了接口的默认方法,使得我们可以在接口中定义方法的默认实现。这让接口不再是“纯抽象”的概念,为接口设计提供了更多的灵活性。
场景:计算机的可充电设备
public interface RechargeableDevice {void recharge();// 默认方法:检查设备电量default void checkBatteryStatus() {System.out.println("Battery status: Good");}
}public class Phone implements RechargeableDevice {@Overridepublic void recharge() {System.out.println("Phone is recharging");}
}
在这个例子中,Phone
类只需要实现recharge()
方法,而checkBatteryStatus()
方法可以直接使用接口中的默认实现。这在需要扩展接口功能时非常有用,避免破坏现有的实现。
场景:有些公共的方法可以抽取到接口层
有时候可以将一些公共的方法抽取到接口层,采用 default 方法实现,然后子类中实现该接口可以复用,也可以简化代码编写。
EBRemoteRpcInvokerSupport
接口层如下:
public interface EBRemoteRpcInvokerSupport {default IEtFormDatasetService iEtFormDatasetService() {return RemoteEBRpcInvoker.newInstance(IEtFormDatasetService.class);}default RemoteSimpleDataService remoteSimpleDataService() {return RemoteEBRpcInvoker.newInstance(RemoteSimpleDataService.class);}default EBPermissionForCustomerService ebPermissionForCustomerService() {return RemoteEBRpcInvoker.newInstance(EBPermissionForCustomerService.class);}default RightService rightService() {return RemoteEBRpcInvoker.newInstance(RightService.class);}default EbFormObjDubboService ebFormObjDubboService() {return RemoteEBRpcInvoker.newInstance(EbFormObjDubboService.class);}default RemoteEBObjService remoteEBObjService() {return RemoteEBRpcInvoker.newInstance(RemoteEBObjService.class);}default EbFormLayoutDubboService ebFormLayoutDubboService() {return RemoteEBRpcInvoker.newInstance(EbFormLayoutDubboService.class);}default ButtonDubboService buttonDubboService() {return RemoteEBRpcInvoker.newInstance(ButtonDubboService.class);}default RemoteEBFormApiService remoteEBFormApiService() {return RemoteEBRpcInvoker.newInstance(RemoteEBFormApiService.class);}default RemoteLogicSqlService remoteLogicSqlService() {return RemoteEBRpcInvoker.newInstance(RemoteLogicSqlService.class);}default EBDataPermissionDubboService ebDataPermissionDubboService() {return RemoteEBRpcInvoker.newInstance(EBDataPermissionDubboService.class);}
}
MultiLayoutServiceImpl
子类使用如下:
@Slf4j
@Service
public class MultiLayoutServiceImpl implements MultiLayoutService, EBRemoteRpcInvokerSupport {@Overridepublic Map<String, String> getOriginLayoutId() {// ...List<FormTableDto> formTableList = remoteEBFormApiService().listFormTable(param, UserContext.getCurrentUser());// ...return map;}
}
DataFetchingSupport
接口代码如下:
public interface DataFetchingSupport {ObjectMapper objectMapper = new ObjectMapper();Pageable defaultPageable = PageRequest.of(0, 20);default ObjectMapper getObjectMapper() {return objectMapper;}default Long extractId(DataFetchingEnvironment environment) {return Long.valueOf(environment.getArgument("id"));}default <T> T extractArg(DataFetchingEnvironment environment, String argName, Class<T> argType) {return getObjectMapper().convertValue(environment.getArgument(argName),argType);}default <T> Optional<T> extract(DataFetchingEnvironment environment, String argName, Class<T> argType) {if (!environment.containsArgument(argName)) {return Optional.empty();}return Optional.of(extractArg(environment, argName, argType));}default Pageable extractPageable(DataFetchingEnvironment environment) {return extract(environment, "page", PageInput.class).map(PageInput::toPageable).orElse(defaultPageable);}default <S, T> DataFetcher<T> fetch(Function<S, T> fetcher) {return environment -> fetcher.apply(environment.getSource());}
}
BusinessArtistTypeWiring
子类使用如下:
@Component
public class BusinessArtistTypeWiring implements TypeWiringBuilder, DataFetchingSupport {private final String cdnBaseUrl;public BusinessArtistTypeWiring(@Value("${artmart.cdn.base-url}") String cdnBaseUrl) {this.cdnBaseUrl = cdnBaseUrl;}@Overridepublic TypeRuntimeWiring build() {return TypeRuntimeWiring.newTypeWiring("Artist").dataFetcher("avatar",fetch((Artist artist) -> ImageUploaded.fromEntity(artist.getAvatar(), cdnBaseUrl))).build();}
}
接口组合(Interface Segregation)
接口组合是一种设计原则,它强调将大接口拆分为多个小接口,避免单一接口承担过多职责。这样做的好处是客户端可以选择实现最相关的接口,而不是被迫实现不必要的方法。
场景:智能家居设备
public interface Switchable {void turnOn();void turnOff();
}public interface Dimmable {void dim(int level);
}public class SmartLight implements Switchable, Dimmable {@Overridepublic void turnOn() {System.out.println("Light is on");}@Overridepublic void turnOff() {System.out.println("Light is off");}@Overridepublic void dim(int level) {System.out.println("Light dimmed to level: " + level);}
}
在这种情况下,SmartLight
可以同时实现两个接口,而其他不需要调光功能的设备则只需实现Switchable
接口。这种设计增强了系统的灵活性和可扩展性。
函数式接口与 Lambda 表达式
Java 8 引入了函数式接口(Functional Interfaces),结合 Lambda 表达式,使代码更加简洁和可读。函数式接口只有一个抽象方法,但可以拥有多个默认方法或静态方法。
场景:异步任务处理
@FunctionalInterface
public interface Task {void execute();
}public class TaskRunner {public void runTask(Task task) {task.execute();}
}public class Main {public static void main(String[] args) {TaskRunner runner = new TaskRunner();// 使用 Lambda 表达式runner.runTask(() -> System.out.println("Task is running"));}
}
这里我们使用函数式接口Task
结合 Lambda 表达式,简化了异步任务的定义和执行,代码更加简洁、直观。
接口驱动的工厂模式
工厂模式是一种常见的设计模式,用于创建对象。通过接口驱动的工厂模式,我们可以将对象创建过程与具体类解耦,使代码更具扩展性。
场景:不同的消息发送方式
public interface MessageSender {void sendMessage(String message);
}public class EmailSender implements MessageSender {@Overridepublic void sendMessage(String message) {System.out.println("Sending email: " + message);}
}public class SmsSender implements MessageSender {@Overridepublic void sendMessage(String message) {System.out.println("Sending SMS: " + message);}
}public class MessageSenderFactory {public static MessageSender getSender(String type) {if (type.equalsIgnoreCase("email")) {return new EmailSender();} else if (type.equalsIgnoreCase("sms")) {return new SmsSender();}throw new IllegalArgumentException("Unknown sender type");}
}
通过工厂方法,客户端代码不需要关心具体的发送方式,只需通过接口与工厂类进行交互即可。
上述代码可以简化成函数式接口编写
, 代码如下:
public interface MessageSender {void sendMessage(String message);
}class EmailSender implements MessageSender {@Overridepublic void sendMessage(String message) {System.out.println("Sending email: " + message);}
}class SmsSender implements MessageSender {@Overridepublic void sendMessage(String message) {System.out.println("Sending SMS: " + message);}
}@Configuration
class MessageSenderFactory {public static Map<String, Supplier<MessageSender>> funcOfMap = new ConcurrentHashMap<>();@PostConstructpublic void init() {MessageSender.funcOfMap.put("email", EmailSender::new);MessageSender.funcOfMap.put("sms", SmsSender::new);}public static MessageSender getServiceBean(String type) {return MessageSender.funcOfMap.get(type).get();}
}
省去了很多的 if-else
代码。
接口与依赖注入
在 Spring 等框架中,接口常常用于依赖注入(Dependency Injection, DI),这是一种非常灵活的设计方式。通过注入接口,框架可以在运行时动态提供具体实现,极大增强了代码的可测试性和模块化。
场景:Spring 中的依赖注入
public interface UserService {void createUser(String name);
}@Service
public class UserServiceImpl implements UserService {@Overridepublic void createUser(String name) {System.out.println("Creating user: " + name);}
}@Controller
public class UserController {@Autowiredprivate UserService userService;public void handleRequest(String name) {userService.createUser(name);}
}
通过使用接口和注入机制,UserController
与UserServiceImpl
之间没有直接的依赖,提升了代码的解耦和灵活性。
结语
Java 接口的巧妙应用不仅仅局限于定义行为契约。通过灵活使用接口,我们可以实现更好的代码结构,支持多态、依赖注入、工厂模式、函数式编程等多种编程范式。理解并善用接口,可以让你的代码更加优雅、灵活和易于维护。
希望本文为你提供了一些关于 Java 接口的巧妙应用思路,让你的代码设计更上一层楼!
推荐阅读文章
1、使用 Spring 框架构建 MVC 应用程序:初学者教程
2、有缺陷的 Java 代码:Java 开发人员最常犯的 10 大错误
3、如何理解应用 Java 多线程与并发编程?
4、Java Spring 中常用的 @PostConstruct 注解使用总结
5、线程 vs 虚拟线程:深入理解及区别
6、深度解读 JDK 8、JDK 11、JDK 17 和 JDK 21 的区别
7、10大程序员提升代码优雅度的必杀技,瞬间让你成为团队宠儿!
8、“打破重复代码的魔咒:使用 Function 接口在 Java 8 中实现优雅重构!”
9、Java 中消除 If-else 技巧总结
10、线程池的核心参数配置(仅供参考)
11、【人工智能】聊聊Transformer,深度学习的一股清流(13)
12、Java 枚举的几个常用技巧,你可以试着用用