2

I have a multilevel map requirement and I am using Guava Table . More precisely HashBasedTable.

Since my code needs a lot of custom processing on this dataset I would like to implement a derived class e.g. EventActionRuleTable which holds a map of Event - Action objects against a source.

something like this

HashMap<Source , Map<Event, Action> ruleMap ; 

I am replacing the above with a

Table<Source, Event , Action> ruleTable = HashBasedTable.create();

But to hold all my custom code I would like to subclass HashBasedTable and figured its simply not possible.

Thus I choose to go with a delegate i.e.

public EventActionRule extends Table<Source, Event, Action>{

private HashBasedTable<Source, Event, Action> backup = null ;

public HashBasedTable<Source, Event, Action> getBackupTable() {

    if (backupTable == null) {
        backupTable = HashBasedTable.create() ;
    }

    return backupTable;
}  

@Override
public boolean isEmpty() {
    return getBackupTable().isEmpty();
}

/**
All other methods of Table interface overridden to delegate calls to backup instance
*/
  ....
}
  1. Is this approach correct ? Can you list issues if its not ? Any alternative approach ?

  2. Is HashBasedTable Gwt serialization compatible ? I am asking since the two backup maps used internally in the HashBasedTable are annotated with @GwtTransient annotation.

Gautam
  • 1,030
  • 13
  • 37

1 Answers1

6

Ad 1. Your approach is correct, although you can use built-in Guava solution for using delegates - Forwarding Decorators:

For all the various collection interfaces, Guava provides Forwarding abstract classes to simplify using the decorator pattern.

In your case, ForwardingTable is waiting for you:

public static class EventActionRule extends ForwardingTable<Source, Event, Action> {

    private Table<Source, Event, Action> delegate = HashBasedTable.create();

    @Override
    protected Table<Source, Event, Action> delegate() {
        return delegate;
    }

    // just an example: isEmpty (and other methods) is ready to be overriden
    @Override
    public boolean isEmpty() {
        boolean isEmpty = delegate().isEmpty();
        System.out.println("Was map empty? " + isEmpty);
        return isEmpty;
    }
}

Ad. 2. Yes, HashBasedTable is serializable under GWT.

Grzegorz Rożniecki
  • 27,415
  • 11
  • 90
  • 112
  • 2
    Just to note: you don't have to override `isEmpty()` at all with `ForwardingTable` unless you need to change its behavior. `return delegate().isEmpty()` is what it'll do by default. – ColinD Mar 30 '15 at 16:35
  • @Xaerxess a question arises - what is the purpose of GwtTransient annotation applied on the internal backup maps then ? – Gautam Mar 31 '15 at 02:37
  • @ColinD Of course I didn't mean to suggest overriding `isEmpty` is mandatory, I edited my answer to clarify that. – Grzegorz Rożniecki Mar 31 '15 at 09:08
  • @Gautam To be honest, I don't know. Its docs in Guava says: _Private replacement for `com.google.gwt.user.client.rpc.GwtTransient` to work around build-system quirks. This annotation should be used only in `com.google.common.collect`._ I think it's just an implementation detail, but maybe @ColinD could answer that more accurately. – Grzegorz Rożniecki Mar 31 '15 at 09:11
  • 2
    @Gautam @Xaerxess: `HashBasedTable` is marked as `@GwtCompatible(serializable = true)`, so it is GWT serializable. As to why the fields are transient for GWT, the class has a custom GWT serializer: [`HashBasedTable_CustomFieldSerializer`](https://github.com/google/guava/blob/master/guava-gwt/src/com/google/common/collect/HashBasedTable_CustomFieldSerializer.java). – ColinD Mar 31 '15 at 16:42