Android SQLite数据模型父类访问子类静态变量及类名问题
看起来你在实现Android SQLite模型基类的时候踩了静态变量和反射的坑,我来帮你理清楚问题所在,再给出适合你需求的解决方案:
问题分析与解决方案
一、为什么table变量始终为null?
你当前的代码存在两个核心逻辑错误:
- 子类静态块没有正确赋值父类变量:子类
User的静态块里写的String table = "test_users_table";是一个局部变量,完全没有对父类DataModel的静态table变量赋值。就算改成static String table = "test_users_table";,这也只是子类自己的静态变量——静态变量不支持“重写”,子类的同名静态变量只会隐藏父类的,父类方法里访问的还是自己的table。 - 关于"明明为null却进入if分支":大概率是测试时的代码缓存或意外赋值导致的,但核心问题还是静态变量的设计逻辑错误。
二、为什么获取的是datamodels而不是子类类名?
你在getTableName()里用DataModel.class.getSimpleName(),直接获取的是父类DataModel的类名,转小写加s后自然是datamodels。要拿到子类的类名,必须获取运行时实际子类的Class对象,而不是父类的。
三、符合你需求的正确实现方案
考虑到你要封装find、save等通用SQL操作,这里推荐两种可靠的方式:
方案1:抽象方法让子类提供表名(无反射,直观易懂)
把父类改成抽象类,定义抽象方法让子类返回专属表名,同时用泛型保证类型安全:
public abstract class DataModel<T> { // 子类必须实现该方法返回自己的表名 protected abstract String getTableName(); // 通用find方法示例 public static <T extends DataModel<T>> T find(Class<T> clazz, long id) throws IllegalAccessException, InstantiationException { DataModel<T> model = clazz.newInstance(); String tableName = model.getTableName(); // 这里编写你的SQL查询逻辑,比如: // String sql = "SELECT * FROM " + tableName + " WHERE id = ?"; System.out.println("查询表:" + tableName); return (T) model; } } // 子类实现 public class User extends DataModel<User> { @Override protected String getTableName() { return "test_users_table"; } }
调用方式:User user = User.find(User.class, 1L);
方案2:注解标记表名(更优雅,适合大量子类)
先定义一个表名注解:
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface TableName { String value(); }
修改父类通过反射获取子类的注解值:
public class DataModel<T> { public static <T extends DataModel<T>> String getTableName(Class<T> clazz) { TableName annotation = clazz.getAnnotation(TableName.class); if (annotation != null) { return annotation.value(); } // 无注解时使用默认规则:类名转小写加s return clazz.getSimpleName().toLowerCase() + "s"; } // 通用find方法 public static <T extends DataModel<T>> T find(Class<T> clazz, long id) { String tableName = getTableName(clazz); // 编写SQL逻辑 System.out.println("查询表:" + tableName); try { return clazz.newInstance(); } catch (Exception e) { e.printStackTrace(); return null; } } } // 子类用注解标记表名 @TableName("test_users_table") public class User extends DataModel<User> { }
调用方式同样是:User user = User.find(User.class, 1L);
四、为什么不推荐用静态变量传递表名?
静态变量属于类本身,而你的需求是每个子类对应不同的表,用静态变量会导致父子类的静态变量互相干扰,多子类场景下极易出现表名混乱的问题。上面两种方案能更好地隔离每个子类的表名,同时符合面向对象设计原则。
内容的提问来源于stack exchange,提问作者Javier Torron Diaz
相关产品推荐
相关产品推荐

