Cake pattern with Java8 possible?

With inspiration from other answers I came up with the following (rough) class hierarchy that is similar to the cake pattern in Scala:

interface UserRepository {
    String authenticate(String username, String password);
}

interface UserRepositoryComponent {
    UserRepository getUserRepository();
}

interface UserServiceComponent extends UserRepositoryComponent {
    default UserService getUserService() {
        return new UserService(getUserRepository());
    }
}

class UserService {
    private final UserRepository repository;

    UserService(UserRepository repository) {
        this.repository = repository;
    }

    String authenticate(String username, String password) {
        return repository.authenticate(username, password);
    }
}

interface LocalUserRepositoryComponent extends UserRepositoryComponent {
    default UserRepository getUserRepository() {
        return new UserRepository() {
            public String authenticate(String username, String password) {
                return "LocalAuthed";
            }
        };
    }
}

interface MongoUserRepositoryComponent extends UserRepositoryComponent {
    default UserRepository getUserRepository() {
        return new UserRepository() {
            public String authenticate(String username, String password) {
                return "MongoAuthed";
            }
        };
    }
}

class LocalApp implements UserServiceComponent, LocalUserRepositoryComponent {}
class MongoApp implements UserServiceComponent, MongoUserRepositoryComponent {}

The above compiles on Java 8 as of Jan.9 2013.


So, can Java 8 do a cake-like pattern? Yes.

Is it as terse as Scala, or as effective as other patterns in Java (i.e. dependency injection)?
Probably not, the above sketch required a whole lot of files and is not as terse as Scala.

In summary:

  • Self-types (as needed for the cake pattern) can be emulated by extending the base interface we expect.
  • Interfaces cannot have inner classes (as noted by @Owen), so instead we can use anonymous classes.
  • val and var can be emulated by using a static hashmap (and lazy initialization), or by the client of the class simply storing the value on their side (like UserService does).
  • We can discover our type by using this.getClass() in a default interface method.
  • As @Owen notes, path dependent types are impossible using interfaces, so a full cake pattern is inherently impossible. The above shows, however, that one could use it for dependency injection.

Leave a Comment

tech