0

I currently face the following issue:

I am trying to refactor a recursive algorithm to an iterative one. What this recursive algorithm does is this:

method1 is passed some initial parameters. Based on a processing that takes place at the beginning of method1, method2 is invoked with these parameters. Now method2 uses some conditions and based on the one that is satisfied method1 is invoked again with the appropriate parameters.

Now, based on the answer on the link I've provided above I did the same thing. But I have to pass parameters around so I did this:

Stack<ArrayList<Object>> stack (the stack for the ArrayList<Object> objects)

ArrayList<Object> parametersForSync = new ArrayList<Object>();
ArrayList<Object> paramForHandle = new ArrayList<Object>();

(Each array list of objects is a list of the parameters to be passed to both the methods. The first array list is for the first method and the second for the second method.)

Assuming I pop and push array lists down the stack correctly I face the following issue which is my main problem and the reason of this question:

Within method2 I have to check whether the object (that was on the array list and is passed to the method) is an instanceof another class of mine. Now I have some conditions there which do not get satisfied when in fact they should.

Is this because of java's type erasure? Is there anyway to overcome this?

If am not clear at a certain point in my explanations please ask me to clarify.

Edit: What follows is the code that replaces the recursion that goes like this:

syncWithServer(parameter set x){
    handleResultArray(parameter set y);
};
handleResultArray(parameter set ){
    syncWithServer(parameter set w)
}

===========================================================

Stack<ArrayList<Object>> stack = new Stack<ArrayList<Object>>();
ArrayList<Object> paramList = new ArrayList<Object>();
paramList.add(oeHelper);
paramList.add(false);
paramList.add(domain);
paramList.add(null);
paramList.add(true);
paramList.add(10000);
paramList.add(true);

stack.push(paramList);
int counter = 0;
ArrayList<Object> parametersForSync = new ArrayList<Object>();
ArrayList<Object> paramForHandle = new ArrayList<Object>();
while (!stack.isEmpty()) {
    Log.d(TAG, "Loop: " + counter);
    parametersForSync = stack.pop();
    paramForHandle = ((OEHelper) parametersForSync.get(0))
            .syncWithServer(
        // why error here?
        (boolean) parametersForSync.get(1),
        (OEDomain) parametersForSync.get(2),
        (List<Object>) parametersForSync.get(3),
        (boolean) parametersForSync.get(4),
        (int) parametersForSync.get(5),
        (boolean) parametersForSync.get(6));

    parametersForSync = ((OEHelper) paramForHandle.get(3))
            .handleResultArray(
        (OEFieldsHelper) paramForHandle.get(0),
        (JSONArray) paramForHandle.get(1),
        (boolean) paramForHandle.get(2));

    if (parametersForSync.size() != 0) {
        stack.push(parametersForSync);
    }
    counter++;

Now the first method:

public ArrayList<Object> syncWithServer(boolean twoWay, OEDomain domain,
        List<Object> ids, boolean limitedData, int limits,
        boolean removeLocalIfNotExists) {
    Log.d(TAG, "syncWithServer");

    List<OEColumn> dbCols = mDatabase.getDatabaseColumns();
    List<OEColumn> dbFinalList = new ArrayList<OEColumn>();
    ArrayList<Object> parametersList = new ArrayList<Object>();

    Log.d(TAG, "Columns & finalList created");
    for (OEColumn col : dbCols) {
        if (!mOne2ManyCols.contains(col.getName())) {
            dbFinalList.add(col);
        }
    }

    OEFieldsHelper fields = new OEFieldsHelper(dbFinalList);
    try {
        if (domain == null) {
            domain = new OEDomain();
        }
        if (ids != null) {
            domain.add("id", "in", ids);
        }
        if (limitedData) {
            mPref = new PreferenceManager(mContext);
            int data_limit = mPref.getInt("sync_data_limit", 60);
            domain.add("create_date", ">=",
                    OEDate.getDateBefore(data_limit));
        }
        if (limits == -1) {
            limits = 50;
        }
        Log.d(TAG, "*****.search_read() started");
        JSONObject result = *****.search_read(mDatabase.getModelName(),
                fields.get(), domain.get(), 0, limits, null, null);
        Log.d(TAG, "***.search_read() returned");
        mAffectedRows = result.getJSONArray("records").length();
        parametersList.add(fields);
        parametersList.add(result.getJSONArray("records"));
        parametersList.add(removeLocalIfNotExists);
        parametersList.add(OEHelper.this);

//This parametersList contains the parameters that must be used to invoke the next method

Now the second method:

=================================================

public ArrayList<Object> handleResultArray(
        OEFieldsHelper fields, JSONArray results,
        boolean removeLocalIfNotExists) {
    Log.d(TAG, "handleResultArray");

    ArrayList<Object> parametersList = new ArrayList<Object>();
    // ArrayList<Object> parameterStack = new ArrayList<Object>();
    try {
        fields.addAll(results);
        List<OERelationData> rel_models = fields.getRelationData();
        Log.d(TAG, "rel_models: "+rel_models.size());
        for (OERelationData rel : rel_models) {

            // Handling many2many records

            if (rel.getDb().getClass()==OEManyToMany.class
                    /*instanceof OEManyToMany*/) {//TODO type erasure?
                Log.v(TAG, "Syncing ManyToMany Records");
                OEManyToMany m2mObj = (OEManyToMany) rel.getDb();
                OEHelper oe = ((OEDatabase) m2mObj.getDBHelper())
                        .getOEInstance();

                parametersList.add(oe);
                parametersList.add(false);
                parametersList.add(null);
                parametersList.add(rel.getIds());
                parametersList.add(false);
                parametersList.add(0);
                parametersList.add(false);

                return parametersList;
            } else if (rel.getDb().getClass()==OEManyToOne.class
                    /*instanceof OEManyToOne*/) {

                // Handling many2One records
                Log.v(TAG, "Syncing ManyToOne Records");
                // M2OCounter++;
                OEManyToOne m2oObj = (OEManyToOne) rel.getDb();
                OEHelper oe = ((OEDatabase) m2oObj.getDBHelper())
                        .getOEInstance();
                parametersList.add(oe);
                parametersList.add(false);
                parametersList.add(null);
                parametersList.add(rel.getIds());
                parametersList.add(false);
                parametersList.add(0);
                parametersList.add(false);
                // parametersMap.put(Counter, parametersList);
                // parameterStack.add(parametersList);
                return parametersList;
            } else if (rel.getDb().getClass()==OEOneToMany.class
                    /*instanceof OEOneToMany*/) {

                Log.v(TAG, "Syncing OneToMany Records");
                // O2MCounter++;
                OEOneToMany o2mObj = (OEOneToMany) rel.getDb();
                OEHelper oe = ((OEDatabase) o2mObj.getDBHelper())
                        .getOEInstance();
                oe.setOne2ManyCol(o2mObj.getColumnName());
                parametersList.add(oe);
                parametersList.add(false);
                parametersList.add(null);
                parametersList.add(rel.getIds());
                parametersList.add(false);
                parametersList.add(0);
                parametersList.add(false);
                // parametersMap.put(Counter, parametersList);
                // parameterStack.add(parametersList);
                return parametersList;
            } else {

                Log.v(TAG, "Syncing records with no relations"
                        + rel.getDb().getClass().getSimpleName());
                OEHelper oe = ((OEDatabase) rel.getDb()).getOEInstance();
                parametersList.add(oe);
                parametersList.add(false);
                parametersList.add(null);
                parametersList.add(rel.getIds());
                parametersList.add(false);
                parametersList.add(0);
                parametersList.add(false);
                return parametersList;//TODO when nothing matches this returns
            }
        }
        List<Long> result_ids = mDatabase.createORReplace(
                fields.getValues(), removeLocalIfNotExists);
        mResultIds.addAll(result_ids);
        mRemovedRecordss.addAll(mDatabase.getRemovedRecords());

    } catch (Exception e) {
        e.printStackTrace();
    }
    return parametersList;
}

The second method is supposed to return from within one of the conditions but none of the conditions is met

Community
  • 1
  • 1
George Daramouskas
  • 3,720
  • 3
  • 22
  • 51
  • 6
    I personally cannot answer this without seeing the actual code. – EpicPandaForce Apr 03 '15 at 12:18
  • 3
    Hi George. This sounds like an incredibly complex issue that needs to be boiled down with a simple, compilable example of the *core concept*. – christopher Apr 03 '15 at 12:21
  • 1
    The whole benefit of using generics in Java is to avoid the use of `Object` and `instanceof` and casting from one type to another. Can't you replace `Object` with a specific type suited to your problem, or make your methods generic (so that the client code can specify the payload type directly)? – Bobulous Apr 03 '15 at 12:22
  • Please provide specific code example. – Crazyjavahacking Apr 03 '15 at 12:24
  • @Adding code in a minute – George Daramouskas Apr 03 '15 at 12:26
  • You should definitely replace both `ArrayList` with dedicated pojo's to hold everything. The ArrayList usage here seem to be a lazy solution to avoid defining those pojo's, but in the end you are spending much more time with code difficult to read and casts everywhere. – Didier L Apr 16 '15 at 11:59

1 Answers1

0

No - it is not a type erasure problem. As Oracle says:

Type erasure ensures that no new classes are created for parameterized types; consequently, generics incur no runtime overhead.

So at runtime your classes are just plain classes and your objects are what they are, but it is not going to remove the base type information.

Why not put a log statement in and print out the class of the object?

rghome
  • 8,529
  • 8
  • 43
  • 62
  • `it is not going to remove the base type information.` Yes, but my base type here is `Object` because the objects in my array list are of no common inheritance. Thus every other type information but object's is removed. Right? – George Daramouskas Apr 03 '15 at 13:56
  • The `instanceof` will look at the type of the actual object stored in the array (which never gets erased), not the type of the array parameter in the generics declaration (which does get erased and isn't any use anyway). – rghome Apr 03 '15 at 13:59
  • Let me check on that and will get back to you. – George Daramouskas Apr 03 '15 at 14:15
  • The `actual object stored in the array` doesn't get it's type erased? and what do you mean by `the type of the array parameter`? – George Daramouskas Apr 03 '15 at 15:56
  • I mean the `` bit. The system can't tell between an `ArrayList` an `ArrayList` and an `ArrayList`; it is just an `ArrayList`. But you are not looking at the array type anyway, you are looking at the object actually in the array element. – rghome Apr 03 '15 at 16:00
  • It truly wasn't a type erasure problem. We refactored the whole thing for it was mind-bending/non readable. – George Daramouskas Apr 16 '15 at 12:42