I just wonder: with Java 8, and the possibility to add implementation in interfaces (a bit like Scala traits), will it be possible to implement the cake pattern, like we can do in Scala?
If it is, can someone provide a code snippet?
I just wonder: with Java 8, and the possibility to add implementation in interfaces (a bit like Scala traits), will it be possible to implement the cake pattern, like we can do in Scala?
If it is, can someone provide a code snippet?
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:
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).this.getClass()
in a default interface method.Maybe you can do something like this in Java 8
interface DataSource
{
String lookup(long id);
}
interface RealDataSource extends DataSource
{
default String lookup(long id){ return "real#"+id; }
}
interface TestDataSource extends DataSource
{
default String lookup(long id){ return "test#"+id; }
}
abstract class App implements DataSource
{
void run(){ print( "data is " + lookup(42) ); }
}
class RealApp extends App implements RealDataSource {}
new RealApp().run(); // prints "data is real#42"
class TestApp extends App implements TestDataSource {}
new TestApp().run(); // prints "data is test#42"
But it is in no way better than the plain/old approach
interface DataSource
{
String lookup(long id);
}
class RealDataSource implements DataSource
{
String lookup(long id){ return "real#"+id; }
}
class TestDataSource implements DataSource
{
String lookup(long id){ return "test#"+id; }
}
class App
{
final DataSource ds;
App(DataSource ds){ this.ds=ds; }
void run(){ print( "data is " + ds.lookup(42) ); }
}
new App(new RealDataSource()).run(); // prints "data is real#42"
new App(new TestDataSource()).run(); // prints "data is test#42"
I did a small proof-on-concept on this recently. You can see the blog post here: http://thoredge.blogspot.no/2013/01/cake-pattern-in-jdk8-evolve-beyond.html and the github repo here: https://github.com/thoraage/cake-db-jdk8
Basically you can do it, but you face at least two obstacles that makes it less slick than Scala. Firstly the Scala traits can have state and Java's interface can't. Many modules need state. This can be fixed by creating a general state component to hold this information, but this will need to be in a class. At least in part. Second issue is that a nested class in an interface is more akin to a static nested class in class. So you can't access the interfaces methods directly from the module class. The default interface method have access to this scope and can add this to the constructor of the module class.
A few experiments suggest no:
Nested classes are automatically static. This is inherently uncakelike:
interface Car {
class Engine { }
}
// ...
Car car = new Car() { };
Car.Engine e = car.new Engine();
error: qualified new of static class
Car.Engine e = car.new Engine();
So, apparently, are nested interfaces, although it's harder to coax out the error messages:
interface Car {
interface Engine { }
}
// ...
Car car = new Car() { };
class Yo implements car.Engine {
}
error: package car does not exist
class Yo implements car.Engine {
// ...
class Yo implements Car.Engine {
}
// compiles ok.
So, without instance member classes, you do not have path dependent types, which is basically necessary for the cake pattern. So at least, no, not in the straightforward way, it is not possible.
Ignoring the new functionality in Java 8 you can in theory do the Cake Pattern in Java 5 and above using compile time AspectJ ITDs.
AspectJ DTO's allow you to make Mixins. The only annoying thing is that you will have to make two artifacts: the aspect (ITD) and the interface. However ITDs allow you to do some crazy stuff like add annotations to classes that implement an interface.
© 2022 - 2024 — McMap. All rights reserved.
this
andthis.getClass()
in a default method body, and you can add extra state through a weak identity map. However in the logging example, it is just not the java way; nothing wrong with the plain/old solution of adding an instance fieldfinal Logger logger=Logger.of(this);
to achieve the mixin effect. – Britzka