背景设定
我正在尝试创建一个用于在Android中访问和缓存Web服务数据的库,并且偶然发现了一个问题。
我的库管理的所有对象都从基类Entity
类继承。任何Entity
的派生类都可以声明其他字段 *或属性 *。
基类负责返回要从缓存(SQLite或File)或Web服务(REST或SOAP)读取的Entity
属性。在我当前的实现中,Entity
类使用反射执行此操作:它读取所有使用@Attribute
注释标记的类字段名称。
问题
我在实现中遇到的问题如下:每次使用像Entity
这样的函数请求所有static Set<String> getAttributes()
的属性时,此函数必须创建{{1实现(目前使用Set
进行快速查找)。
我希望每次发出对所有属性的请求时都避免分配和初始化HashSet
。
从我的角度来看,属性集特定于整个Set<String>
类,而不是Entity
对象。即:拥有Entity
,所有class Customer extends Entity
实例都具有相同的确切属性 - 每个实例都有一个名称,一个地址等等。
我目前的尝试是在每个扩展Customer
的类中声明static Set<String> attributes
,并使用类Entity
块中的反射初始化Set
,如下所示:
static
鉴于要初始化的类,class Customer extends Entity
{
private static final Set<String> attributes = new HashSet<String>();
static
{
Entity.populateAttributes(Customer.class);
}
}
类可以查找该类中标有Entity
注释的所有字段及其基类(例如@Attribute
和Customer extends Person
将使用为Person extends Entity
类声明的属性和从Customer.attributes
类继承的属性填充Customer
集合。
我遇到的问题是,可能没有为扩展Person
的类定义attributes
集,我想在编译时强制执行。
我已经看到这是为Eclipse中的Entity
接口完成的:当创建一个实现Serializable
的类时,如果您的类没有声明Serializable
,Eclipse会显示警告。
问题
如果某个类没有声明字段,是否有任何方法可以强制执行[private] static final long serialVersionUID
类的Serializable
行为并显示警告或(更好)错误?
是否有不同的方法来返回Entity
派生类的属性名称?
FOOTNOTE
* 我将术语Entity
用于不应由库管理的对象属性,将术语field
用于应由库管理的对象属性(可从Web服务或SQLite /文件缓存,并保存在Web服务或SQLite /文件缓存中)
编辑1
基本上,我想要实现的是以有效的方式获得一组attribute
的属性*(参见上面的脚注)。帮助程序类使用此列表将对象值存储在数据库中(Entity
查询可以从属性名称和类型中推断出来)或发送到Web服务。
此库用于缓存来自Web服务的值,并用于同步本地数据库(可能包含额外的用户输入值,可能缺少对象的服务器更新值)以及可通过Web服务访问的数据。它并不是要在具有通用访问器/ mutator的应用程序中替换per-field访问器/ mutator的使用。
这个概念称为键值编码,被许多框架和库使用。作为一个例子,我在Google搜索中找到的使用KVC的库的前两个例子是Cocoa和Sproutcore。参考文献:Apple developer documentation和Sproutcore wiki。
KVC也用于Android开发。 CREATE TABLE
,Bundle
和SQLiteCursor
充分利用KVC。
答案 0 :(得分:0)
由于我可能无法在派生类的定义中强制执行静态字段的声明,因此我将在上述Entity
类的定义中存储属性集和类描述的映射。并确保在Entity
构造函数中初始化派生类的属性集。
举个例子:
public class Entity
{
private final static Map<Class<? extends Entity>, Set<String>> attributes = new HashMap<Class<? extends Entity>, Set<String>>();
public static void populateAttributes(Class<? extends Entity> derivedClass)
{
//initialize the set of attributes for the derived class and
//add it to attributes map with "derivedClass" as key
}
static
{
populateAttributes(Entity.class);
}
public Entity()
{
//calling this.getClass() returns the object's actual (derived) class(*)
if(!attributes.containsKey(this.getClass())
populateAttributes(this.getClass());
}
//rest of class definition (including getAttributes method)
}
public class Customer extends Entity
{
@Attribute
String someAttribute; //will be processed automatically
}
获取Entity
属性的成本从解析所有类属性和创建新集合以使其保持在Map
上的检查和检索操作上减少了,这应该非常小使用HashMap
时。
通过对属性Entity
(快速Map
s)进行检查,可以提高创建HashMap
实例的成本。初始化给定类的attributes
集的成本可以忽略不计,因为每个派生类类型只执行一次。
考虑到在90%或更多场合下每个类实例至少请求一次属性,这应该提供比问题中描述的初始解决方案更好的整体性能。
(*)在基类实例方法中调用this.getClass()
将返回对象的实际类。如果对象已初始化为派生类实例,则将返回派生类的描述(Class
对象)。关于这方面的问题已在此处提出并回答:Which class does getClass() report inside a constructor of a base class。