Make callbacks interfaces rather than abstract classes.

Using interfaces is more idiomatic Java and gives the client
programmer more flexibility.

Originally we used abstract classes with default implementation of
methods to reduce the burden on the client programmers, allowing them
to just implement some of the methods. However using abstract classes
does not allow the client programmer to use some common Java idioms,
such as "mixing in" callbacks from multiple interfaces into a single
class, possibly one of the big classes in their application.

Change-Id: I464f9729a2f70a0355fff9238136a3cb7a6a9cbf
diff --git a/syncbase/src/main/java/io/v/syncbase/Database.java b/syncbase/src/main/java/io/v/syncbase/Database.java
index 8985189..eda76d9 100644
--- a/syncbase/src/main/java/io/v/syncbase/Database.java
+++ b/syncbase/src/main/java/io/v/syncbase/Database.java
@@ -144,21 +144,18 @@
     /**
      * Handles discovered syncgroup invites.
      */
-    public static abstract class SyncgroupInviteHandler {
+    public interface SyncgroupInviteHandler {
         /**
          * Called when a syncgroup invitation is discovered. Clients typically handle invites by
          * calling {@code acceptSyncgroupInvite} or {@code ignoreSyncgroupInvite}.
          */
-        public void onInvite(SyncgroupInvite invite) {
-        }
+        void onInvite(SyncgroupInvite invite);
 
         /**
          * Called when an error occurs while scanning for syncgroup invitations. Once
          * {@code onError} is called, no other methods will be called on this handler.
          */
-        public void onError(Throwable e) {
-            throw new RuntimeException(e);
-        }
+        void onError(Throwable e);
     }
 
     // TODO(sadovsky): Document which thread the handler methods are called on.
@@ -231,13 +228,9 @@
         }
     }
 
-    public static abstract class AcceptSyncgroupInviteCallback {
-        public void onSuccess(Syncgroup sg) {
-        }
-
-        public void onFailure(Throwable e) {
-            throw new RuntimeException(e);
-        }
+    public interface AcceptSyncgroupInviteCallback {
+         void onSuccess(Syncgroup sg);
+         void onFailure(Throwable e);
     }
 
     /**
@@ -389,7 +382,7 @@
     /**
      * Handles observed changes to the database.
      */
-    public static abstract class WatchChangeHandler {
+    public interface WatchChangeHandler {
         // TODO(sadovsky): Consider adopting Aaron's suggestion of combining onInitialState and
         // onChangeBatch into a single method, to make things simpler for developers who don't want
         // to apply deltas to their in-memory data structures:
@@ -399,23 +392,19 @@
          * Called once, when a watch change handler is added, to provide the initial state of the
          * values being watched.
          */
-        public void onInitialState(Iterator<WatchChange> values) {
-        }
+        void onInitialState(Iterator<WatchChange> values);
 
         /**
          * Called whenever a batch of changes is committed to the database. Individual puts/deletes
          * surface as a single-change batch.
          */
-        public void onChangeBatch(Iterator<WatchChange> changes) {
-        }
+        void onChangeBatch(Iterator<WatchChange> changes);
 
         /**
          * Called when an error occurs while watching for changes. Once {@code onError} is called,
          * no other methods will be called on this handler.
          */
-        public void onError(Throwable e) {
-            throw new RuntimeException(e);
-        }
+        void onError(Throwable e);
     }
 
     // TODO(sadovsky): Document which thread the handler methods are called on.
diff --git a/syncbase/src/main/java/io/v/syncbase/Syncbase.java b/syncbase/src/main/java/io/v/syncbase/Syncbase.java
index d889060..be4981f 100644
--- a/syncbase/src/main/java/io/v/syncbase/Syncbase.java
+++ b/syncbase/src/main/java/io/v/syncbase/Syncbase.java
@@ -189,13 +189,9 @@
         throw new UnsupportedOperationException("Not implemented");
     }
 
-    public static abstract class LoginCallback {
-        public void onSuccess() {
-        }
-
-        public void onError(Throwable e) {
-            throw new RuntimeException(e);
-        }
+    public interface LoginCallback {
+        void onSuccess();
+        void onError(Throwable e);
     }
 
     /**
@@ -274,7 +270,7 @@
         }).start();
     }
 
-    private static class UserdataWatchHandler extends Database.WatchChangeHandler {
+    private static class UserdataWatchHandler implements Database.WatchChangeHandler {
         @Override
         public void onInitialState(Iterator<WatchChange> values) {
             onWatchChange(values);
@@ -285,6 +281,11 @@
             onWatchChange(changes);
         }
 
+        @Override
+        public void onError(Throwable e) {
+            throw new RuntimeException(e);
+        }
+
         private void onWatchChange(Iterator<WatchChange> changes) {
             WatchChange watchChange = changes.next();
             if (watchChange.getCollectionId().getName().equals(USERDATA_SYNCGROUP_NAME) &&
@@ -365,14 +366,10 @@
         }
     }
 
-    public static abstract class ScanNeighborhoodForUsersCallback {
-        public abstract void onFound(User user);
-
-        public abstract void onLost(User user);
-
-        public void onError(Throwable e) {
-            throw new RuntimeException(e);
-        }
+    public interface ScanNeighborhoodForUsersCallback {
+        void onFound(User user);
+        void onLost(User user);
+        void onError(Throwable e);
     }
 
     /**