问题
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?
回答1:
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
andvar
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.
回答2:
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"
回答3:
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.
回答4:
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.
回答5:
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.
来源:https://stackoverflow.com/questions/14248766/cake-pattern-with-java8-possible