如何为泛型类创建指定数量的单例风格实例?
解决方案与重构建议
核心思路
采用基于实体类类型的单例容器模式替代多例模式:通过Class<T>作为唯一标识,用线程安全的静态容器缓存每个实体类对应的FileManager实例,确保每个实体类仅对应一个实例,同时兼顾扩展性与代码简洁性。
重构后的代码实现
1. 新增实体类配置注解(解耦配置与代码)
先定义一个注解,用于绑定实体类与配置文件中的属性前缀:
import java.lang.annotation.*; @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface FileStorageConfig { String property(); // 对应application.properties中的属性前缀,例如"manufacturer" }
给实体类添加注解:
@FileStorageConfig(property = "manufacturer") public class Manufacturer implements Serializable { // 类成员与方法 } @FileStorageConfig(property = "product") public class Product implements Serializable { // 类成员与方法 }
2. 重构FileManager类
import java.io.*; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; public class FileManager<T> { // 线程安全的静态容器:缓存每个实体类对应的FileManager实例 private static final Map<Class<?>, FileManager<?>> INSTANCE_CACHE = new ConcurrentHashMap<>(); private final File file; // 私有构造,限制实例创建渠道 private FileManager(String property) throws IOException { this.file = new File(ConfigReader.getProperty(property + ".file.path") .orElseThrow(() -> new IOException(property + " file path is not specified in application.properties."))); } // 公开的实例获取方法,按实体类类型返回唯一实例 @SuppressWarnings("unchecked") public static <T> FileManager<T> getInstance(Class<T> entityClass) throws IOException { return (FileManager<T>) INSTANCE_CACHE.computeIfAbsent(entityClass, clazz -> { // 读取实体类上的注解,获取配置属性前缀 FileStorageConfig config = clazz.getAnnotation(FileStorageConfig.class); if (config == null) { throw new IllegalArgumentException(clazz.getName() + " 未添加@FileStorageConfig注解"); } try { return new FileManager<>(config.property()); } catch (IOException e) { throw new RuntimeException(e); // 适配函数式接口的异常处理 } }); } public void saveToFile(List<T> list) { try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(file))) { oos.writeObject(list); } catch (IOException e) { e.printStackTrace(); } } @SuppressWarnings("unchecked") public List<T> loadFromFile() { // 改为public,供外部调用 if (file.exists()) { try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file))) { return (List<T>) ois.readObject(); } catch (IOException | ClassNotFoundException e) { e.printStackTrace(); } } return new ArrayList<>(); } }
3. 调用示例
// 获取Manufacturer对应的唯一FileManager实例 FileManager<Manufacturer> manufacturerManager = FileManager.getInstance(Manufacturer.class); manufacturerManager.saveToFile(manufacturerList); List<Manufacturer> manufacturers = manufacturerManager.loadFromFile(); // Product同理 FileManager<Product> productManager = FileManager.getInstance(Product.class);
方案优势
- 唯一性与线程安全:借助
ConcurrentHashMap的computeIfAbsent方法,确保每个实体类仅创建一个实例,且多线程环境下无并发问题。 - 扩展性强:新增实体类时,只需添加
@FileStorageConfig注解并配置属性,无需修改FileManager核心代码。 - 配置解耦:通过注解绑定实体类与配置属性,避免硬编码字符串,降低维护成本。
- 简洁高效:利用JDK原生容器实现,无需手动维护多例映射,代码更简洁可靠。
其他可选方案(不推荐)
- 枚举多例:若实体类数量固定且不会扩展,可通过枚举定义实例,但扩展性差,不适用于动态新增实体类的场景。
- DI容器管理:若项目使用Spring等依赖注入框架,可将
FileManager按类型注册为单例,但会增加框架依赖,无DI框架时没必要引入。
内容的提问来源于stack exchange,提问作者vitalii
相关产品推荐
相关产品推荐

