Java接口和抽象类
前言
之前背八股的时候一直都搞不懂抽象类到底有什么用,只能硬背抽象类的概念,接口跟抽象类的区别。。。
最近在实现一个简单的spring框架的时候,突然感觉对接口和抽象类有点理解了。
正文
就拿下面这个继承关系来说,接口的作用是一种规范、一种基准,定义了你能做什么。
而抽象类的作用感觉更多的是职责,抽象类实现一个接口,可以只选择实现一部分方法,不要全部实现,
其他没实现的方法由子类去实现,分工明确,一个抽象类只负责部分功能。并且还可以实现其他接口,实现功能的扩展
这样最后继承抽象类的类就可以获取之前抽象类实现的所有功能,而不用把这些类都写到一个类中,显得臃肿
并且接口 → 抽象类 → 具体实现类这样的结构还利于扩展。
比如:
定义接口(只关心能力)
现在只规定了「你能通过名字拿到一个对象」。
public interface Container {
Object getBean(String name);
}
抽象类(封装公共逻辑)
所有容器都会用「单例缓存」,所以这里把公共逻辑抽出来了。
但怎么创建对象(createBean)留给子类决定。
public abstract class AbstractContainer implements Container {
private final Map<String, Object> singletonObjects = new HashMap<>();
@Override
public Object getBean(String name) {
// 先从缓存找
if (singletonObjects.containsKey(name)) {
return singletonObjects.get(name);
}
// 如果没有,交给子类去创建
Object bean = createBean(name);
singletonObjects.put(name, bean);
return bean;
}
// 留给子类扩展
protected abstract Object createBean(String name);
}
实现类(不同扩展场景)
可以自己扩展不同的对象创建方式(硬编码、XML、注解、远程加载……)。
// 实现1:硬编码方式
public class SimpleContainer extends AbstractContainer {
@Override
protected Object createBean(String name) {
if ("userService".equals(name)) {
return new UserService();
}
throw new RuntimeException("No bean named " + name);
}
}
// 实现2:配置文件方式
public class XmlContainer extends AbstractContainer {
private Map<String, String> beanDefinitions = new HashMap<>();
public XmlContainer() {
beanDefinitions.put("userService", "com.example.UserService");
}
@Override
protected Object createBean(String name) {
try {
String className = beanDefinitions.get(name);
return Class.forName(className).newInstance();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
这样如果以后需要加上其他方式createBean,只需要继承这个抽象类实现createBean()方法就行了。