2

The following is a cleaned up example to reduce this to a single question. There are 3 class files (with a few class shells bunched in there). What does not work is the first argument casting on each of getTable() and getCreateTableList() in the SpanTable class. I would like to know how to get that argument to have its original Span/SpanTable subclass type and be passed into the DbTable calls? Or really, DbTable doesn't need the extra info, but I would like SpanTable, or any caller, to retain its types.

DbRow:

public class DbRow {
    static class Span extends DbRow {}
}

DbTable:

import java.util.ArrayList;

abstract public class DbTable<R extends DbRow> {  

    static class PairList<L, R> { 
        public void addEntry(L s, R t) {  }
        public R getRightForLeft(L left) { return null; }
    }
    static class DbPlatform {  }
    static class DbSelectStatement {    }
    public static class Span extends DbRow { }
    static class TableList<R extends DbRow, T extends DbTable<R>> extends ArrayList<T> {}
    static class PlatformTableList<R extends DbRow, T extends DbTable<R>> 
                    extends PairList<DbPlatform, TableList<R, T>> {}
    static DbSelectStatement getDefaultQuery(String tableName) { return null; }

    public DbTable(DbPlatform platform, String tableName) { }
    public DbSelectStatement getStatement() { return null; }

    /** Return the matching DbTable with matching DbSelectStatement or null */
    static protected DbTable<DbRow> getTable(
            PlatformTableList<DbRow, DbTable<DbRow>> platformList, 
            DbPlatform platform, DbSelectStatement stmt) {
        // Get the table from the list, or create new
        TableList<DbRow, DbTable<DbRow>> list = 
                getCreateTableList(
                 (PlatformTableList<DbRow, DbTable<DbRow>>) platformList, platform);
        // Search the list for a match
        for(DbTable<DbRow> table : list) 
            if(table.getStatement().equals(stmt))
                return table;
        return null; 
    }

    /** Get or create and return a TableList for the Platform.  */
    static protected TableList<DbRow, DbTable<DbRow>> getCreateTableList(
            PlatformTableList<DbRow, DbTable<DbRow>> platformList, DbPlatform platform) { 

        TableList<DbRow, DbTable<DbRow>> list = (TableList<DbRow, DbTable<DbRow>>) 
                platformList.getRightForLeft(platform);
        if(list == null) {
            list = new TableList<DbRow, DbTable<DbRow>>();
            platformList.addEntry(platform, list); 
        }
        return list;
    }
}

SpanTable:

class SpanTable<R extends DbTable.Span> extends DbTable<R> { 

    static private PlatformTableList<Span, SpanTable<Span>> platformList = 
            new PlatformTableList<Span, SpanTable<Span>>();

    static public SpanTable<Span> getCreateSpanTable(DbPlatform platform, String tableName) {

        SpanTable<Span> table = (SpanTable<Span>) getTable(platformList, platform, 
                getDefaultQuery(tableName));
        if(table == null) {
            table = new SpanTable<Span>(platform, tableName);
            getCreateTableList(platformList, platform).add(table);
        }
        return table;
    }

    private SpanTable(DbPlatform platform, String tableName) {
        super(platform, tableName);
    }
}
Mark Meyers
  • 533
  • 3
  • 9
  • 17
  • Since i can not compile that, i can not tell exactly what the problem is. Could you please rewrite the question to a compileable state? I mean, its not a question like "why cant I compile", its a question "why have I to use >". – Grim Feb 20 '17 at 20:10
  • 1
    @PeterRader Now organized into 3 class files. – Mark Meyers Feb 20 '17 at 21:10
  • 1
    @MarkMeyers You cannot say `class TableList> extends ArrayList> {}` because you have to list all the type variables first. The way to do it would be: `class TableList> extends ArrayList>` – Javier Martín Feb 20 '17 at 21:46
  • @JavierMartín, Okay. This seems to work. `class TableList> extends ArrayList {}` – Mark Meyers Feb 20 '17 at 21:54
  • But no doubt the typing issues cascade from there. – Mark Meyers Feb 20 '17 at 22:00
  • Indeed. In those cases you'd want to create static factory functions that can deduce the types of their arguments. – Javier Martín Feb 20 '17 at 22:22

1 Answers1

1

You can make the factory methods inside of DbTable class generic so that they hold on the specific table type (T) passed to them via the platform list:

abstract public class DbTable<R extends DbRow> {  

    protected DbTable(DbPlatform platform, String tableName) {  }

    static class TableList<T extends DbTable<?>> extends ArrayList<T> {}

    static class PlatformTableList<T extends DbTable<?>> 
                    extends PairList<DbPlatform, TableList<T>> {}

    /** Return the matching DbTable with matching DbSelectStatement or null.
     * Will create/add a new TableList if platform not found. */

    static protected <T extends DbTable<?>> T getTable(PlatformTableList<T> platformList, 
            DbPlatform platform, DbSelectStatement stmt) {

        // Get the table from the list, or create new
        TableList<T> list = getCreateTableList(platformList, platform);
        // Search the list for a match
        for(T table : list) {
            if(table.equals(stmt))
                return table;
        }
        return null; 
    }

    /** Get or create and return a TableList for the Platform.  */
    static protected <T extends DbTable<?>> TableList<T> getCreateTableList(
            PlatformTableList<T> platformList, DbPlatform platform) { 

        TableList<T> list = platformList.getRightForLeft(platform);
        if(list == null) {
            list = new TableList<T>();
            platformList.addEntry(platform, list);            
        }
        return list;
    }

}

Now you can also remove the cast in your getCreateSpanTable() method:

SpanTable<Span> table = getTable(platformList, platform, 
                getDefaultQuery(tableName));

As pointed out in a comment: If you want to hold the specific row type R in your TableList class, you can write class TableList<R extends DbRow, T extends DbTable<R>> extends ArrayList<T> {} Also, I would try to avoid extending ArrayList and create a field that holds an ArrayList instead.

Calculator
  • 2,769
  • 1
  • 13
  • 18
  • Thank you. That works great. I am curious as to why you would avoid extending `ArrayList`? – Mark Meyers Feb 21 '17 at 00:25
  • @MarkMeyers Generally, I would favor composition over inheritance when it comes to using collections as discussed [here](http://stackoverflow.com/q/4779173). However, it of course depends. Sometimes, (maybe also in your case) it can make sense to extend `ArrayList` as shown [here](http://stackoverflow.com/a/8098644). – Calculator Feb 21 '17 at 00:44