문제

BACKGROUND SETTING

I am trying to create a library for accessing and caching web service data in Android and have stumbled across a problem.

All the objects managed by my library inherit from a base Entity class. Any derivate class of Entity can declare other fields* or attributes*.

The base class is responsible for returning the Entity attributes that are to be read from cache (SQLite or File) or from a web service (REST or SOAP). In my current implementation, the Entity class performs this using reflection: it reads all class field names that are marked with an @Attribute annotation.

PROBLEM

The issue that I'm having in my implementation is the following: every time that I request all the Entity's attributes using a function like static Set<String> getAttributes(), this function must create a Set implementation (currently using HashSet for that for fast look-up).

I would like to avoid allocating and initialising a Set<String> every time a request for all attributes is made.

From my point of view an attribute set is specific for an entire Entity class, not an Entity object. I.e.: having class Customer extends Entity, all Customer instances have the same exact attributes - each has a name, an address and so on.

My current attempt at this is to declare a static Set<String> attributes in every class that extends Entity and initialise that Set using reflection in the class' static block like this:

class Customer extends Entity 
{
   private static final Set<String> attributes = new HashSet<String>();

   static
   {
       Entity.populateAttributes(Customer.class);
   }
}

Given the class to initialise, the Entity class can look up all fields marked with the @Attribute annotation in that class and it's base classes (e.g. Customer extends Person and Person extends Entity would fill the Customer.attributes set with attributes declared for the Customer class and the ones inherited from the Person class).

The problem I have is that an attributes set might not be defined for a class which extends Entity and I would like to enforce that at compile time.

I have seen this being done for the Serializable interface in Eclipse: when create a class which implements Serializable, Eclipse shows a warning if your class does not declare a [private] static final long serialVersionUID.

QUESTION

Is there any way that I enforce that Serializable behaviour for my Entity class and show a warning or (better) an error if a class has not declared a field?

Is there a different approach to returning the attribute names for an Entity-derived class?

FOOTNOTE

*I used the term field for object properties that should not be managed by the library and the term attribute for object properties that should be managed by the library (be read from a web service or SQLite/File cache and be persisted in a web service or SQLite/File cache)

EDIT 1

Basically, what I am trying to achieve is a to get a set of an Entity's attributes* (see footnote above) in an efficient manner. This list is used by helper classes to store object values in a database (the CREATE TABLE queries can be deduced from the attribute names and types) or to send to a web-service.

This library is used for caching values from a web-service and for synchronizing the local database (which might contain extra user input values and might be missing server updated values for objects) with data accessible through a web-service. It is not intended to replace the use of per-field accessors/mutators in an application with generic accessors/mutators.

This concept is known as Key-Value Coding and is used by many frameworks and libraries. As an example, the first two examples of libraries using KVC that I've found with a Google search are Cocoa and Sproutcore. References: Apple developer documentation and Sproutcore wiki.

KVC is also used in Android development. Bundle, SQLiteCursor and ContentValues make intense use of KVC.

도움이 되었습니까?

해결책

Since I may not be able to enforce the declaration of a static field in the deriving classes' definition, I will go by storing a map of attribute sets and class descriptions in the definition of the Entity class described above and making sure that the set of attributes for a derived class is initialized in the Entity constructor.

As an example:

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
}

The cost of getting an Entitys attributes is reduced from parsing all class attributes and creating a new set to hold them to a check-and-retrieve operation on a Map which should be quite small when using a HashMap.

The cost of creating an Entity instance is increased by a check done to the attributes Map (fast on HashMaps). The cost of initializing the attributes set for a given class is negligible, as it is done only once per derivate class type.

Considering that the attributes are requested at least once per class instance on 90% or more of the occasions, this should give better overall performance than the initial solution described in the question.

(*)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.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top