如何让父类DataModel的all()方法返回子类实例列表?
通用DataModel父类实现方案:泛型+反射适配子类类型
这个需求刚好可以通过泛型方法+抽象类+反射来解决,既能让父类保持通用性,新增子类时也不用修改父类代码,完美符合开闭原则。下面一步步给你拆解实现方案:
1. 重构DataModel为抽象父类
首先把DataModel改成抽象类,定义子类必须实现的核心方法(表名、列定义、数据填充),同时实现通用的all()泛型方法:
import java.lang.reflect.Constructor; import java.util.ArrayList; import java.util.List; import java.util.Map; public abstract class DataModel { // 抽象方法:子类必须返回自己对应的数据库表名 protected abstract String getTableName(); // 抽象方法:子类必须返回自己对应的列名-类型映射 protected abstract Map<String, String> getColumns(); // 抽象方法:子类负责将数据库查询结果填充到自身字段 protected abstract void populate(Map<String, Object> rowData); /** * 通用查询方法:返回指定子类类型的实例列表 * @param modelClass 子类的Class对象 * @return 子类实例组成的ArrayList */ @SuppressWarnings("unchecked") public static <T extends DataModel> ArrayList<T> all(Class<T> modelClass) { ArrayList<T> dataList = new ArrayList<>(); try { // 1. 通过反射创建子类实例,获取表名和列定义 Constructor<T> emptyConstructor = modelClass.getDeclaredConstructor(); T tempInstance = emptyConstructor.newInstance(); String tableName = tempInstance.getTableName(); Map<String, String> columns = tempInstance.getColumns(); // 2. 调用数据库服务查询数据(这里假设getAllMatchesFromTable返回多行数据,类型为List<Map<String, Object>>) List<Map<String, Object>> queryResults = LocalStorageServices.DatabaseService() .getAllMatchesFromTable(tableName, columns); // 3. 遍历查询结果,创建子类实例并填充数据 Constructor<T> columnConstructor = modelClass.getDeclaredConstructor(Map.class); for (Map<String, Object> row : queryResults) { T model = columnConstructor.newInstance(columns); model.populate(row); dataList.add(model); } } catch (Exception e) { // 捕获反射和数据库异常,包装为运行时异常抛出 throw new RuntimeException("Failed to load data for model: " + modelClass.getName(), e); } return dataList; } // 父类构造方法:供子类继承 protected DataModel() {} protected DataModel(Map<String, String> columns) { // 可添加通用初始化逻辑,比如保存列定义 } }
2. 子类实现示例(User类)
子类只需要继承DataModel,实现三个抽象方法,再添加自己的业务字段和方法即可。如果想让调用更简洁,还可以添加一个静态all()方法封装父类调用:
import java.util.HashMap; import java.util.Map; public class User extends DataModel { // 子类业务字段 private String username; private String email; // 数据库表名常量 private static final String TABLE_NAME = "testing"; @Override protected String getTableName() { return TABLE_NAME; } @Override protected Map<String, String> getColumns() { Map<String, String> columns = new HashMap<>(); columns.put("username", "TEXT"); columns.put("email", "TEXT"); return columns; } @Override protected void populate(Map<String, Object> rowData) { // 将查询结果映射到子类字段 this.username = (String) rowData.get("username"); this.email = (String) rowData.get("email"); } // 封装父类方法,让调用更直观 public static ArrayList<User> all() { return DataModel.all(User.class); } // 子类自定义方法 public void sendWelcomeEmail() { System.out.println("Sending email to: " + this.email); } // 构造方法:继承父类 public User() { super(); } public User(Map<String, String> columns) { super(columns); } }
3. 调用方式
现在你可以直接用想要的方式调用,返回的列表不需要强制转换,直接是子类类型:
// 调用子类的all()方法,获取User实例列表 ArrayList<User> appUsers = User.all(); // 直接调用子类的业务方法 for (User user : appUsers) { user.sendWelcomeEmail(); }
4. 新增子类(比如Entry、Post)的适配
当你需要新增Entry或Post模型时,只需要重复User类的步骤:
- 继承
DataModel - 实现
getTableName()、getColumns()、populate()三个抽象方法 - 添加自己的业务字段和方法
- 可选:添加静态
all()方法封装调用
完全不需要修改父类代码,完美适配你的需求。
注意事项
- 确保子类提供了无参构造和接收
Map<String, String>的构造方法(供反射调用) - 数据库查询方法
getAllMatchesFromTable的返回类型建议调整为List<Map<String, Object>>,因为单条Map无法表示多行查询结果(你原来的代码里返回Map<String, Object>可能是个小问题) - 反射过程中的异常建议捕获并包装为运行时异常,避免上层代码处理繁琐的受检异常
内容的提问来源于stack exchange,提问作者Javier Torron Diaz
相关产品推荐
相关产品推荐

