3

背景设置

我正在尝试创建一个用于在 Android 中访问和缓存 Web 服务数据的库,但偶然发现了一个问题。

我的库管理的所有对象都继承自一个基Entity类。任何派生类都Entity可以声明其他字段*或属性*。

基类负责返回Entity要从缓存(SQLite 或文件)或 Web 服务(REST 或 SOAP)读取的属性。在我当前的实现中,Entity该类使用反射执行此操作:它读取所有标有@Attribute注释的类字段名称。

问题

我在实现中遇到的问题如下:每次我Entity使用类似的函数请求所有属性static Set<String> getAttributes()时,该函数必须创建一个Set实现(目前HashSet用于快速查找)。

我想避免在Set<String>每次请求所有属性时分配和初始化 a 。

从我的角度来看,属性集特定于整个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中查找所有标有注释的字段(例如,并将使用为该类声明的属性和从该类继承的属性填充集合)。@AttributeCustomer extends PersonPerson extends EntityCustomer.attributesCustomerPerson

我遇到的问题是,attributes可能没有为扩展的类定义一个集合Entity,我想在编译时强制执行。

我已经看到 Eclipse 中的接口是这样做的Serializable:当创建一个实现的类时Serializable,如果你的类没有声明一个[private] static final long serialVersionUID.

问题

如果一个类没有声明一个字段,有什么方法可以Serializable为我的类强制执行该行为Entity并显示警告或(更好)错误?

是否有不同的方法来返回Entity派生类的属性名称?

脚注

*我使用了field不应由库管理的attribute对象属性的术语和应由库管理的对象属性的术语(从 Web 服务或 SQLite/文件缓存中读取并保存在 Web 服务或 SQLite 中) /文件缓存)

编辑 1

基本上,我想要实现的是以一种有效的Entity方式获得一组 a 的属性*(见上面的脚注)。帮助类使用此列表将对象值存储在数据库中(CREATE TABLE查询可以从属性名称和类型推导出来)或发送到 Web 服务。

该库用于缓存来自 Web 服务的值,并用于将本地数据库(可能包含额外的用户输入值并且可能缺少对象的服务器更新值)与可通过 Web 服务访问的数据同步。它并不打算用通用访问器/修改器替换应用程序中每个字段访问器/修改器的使用。

这个概念被称为键值编码,被许多框架和库使用。例如,我通过 Google 搜索找到的使用 KVC 的前两个库示例是 Cocoa 和 Sproutcore。参考:Apple 开发人员文档Sproutcore wiki

KVC 也用于 Android 开发。BundleSQLiteCursorContentValues大量使用KVC。

4

1 回答 1

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
}

获取Entitys 属性的成本从解析所有类属性并创建一个新集合以将它们保存到对 a 的检查和检索操作减少,Map这在使用 a 时应该非常小HashMap

对属性进行检查(fast on s)Entity会增加创建实例的成本。为给定类初始化集合的成本可以忽略不计,因为每个派生类类型只执行一次。MapHashMapattributes

考虑到在 90% 或更多的情况下,每个类实例至少请求一次属性,这应该比问题中描述的初始解决方案提供更好的整体性能。

(*)calling this.getClass() in a base class instance method will return the object's actual class. If the object has been initialized as a derived class instance, the derived class' description (Class object) will be returned. A question about this has already been asked and answered here: Which class does getClass() report inside a constructor of a base class.

于 2013-03-11T09:16:39.330 回答