I'd also use static methods to declare default functionality for stateless functionality.
For stateful functionality I'd prefer composition instead of inheritance. With composition and using delegates/adapters you can combine default functionality from many sources.
e.g.
public interface StuffDoer{
void doStuff();
void doOtherStuff();
}
public class MyStuffDoer implements StuffDoer{
private final StuffDoer mixin;
public MyStuffDoer(StuffDoer mixin){
this.mixin = mixin;
}
public void doStuff(){
mixin.doStuff();
}
public void doOtherStuff(){
mixin.doOtherStuff();
}
}
public class MyStuffDoer2 implements StuffDoer{
private final StuffDoer mixin1, mixin2;
public MyStuffDoer(StuffDoer mixin1, StuffDoer mixin2){
this.mixin1 = mixin1;
this.mixin2 = mixin2;
}
public void doStuff(){
mixin1.doStuff();
}
public void doOtherStuff(){
mixin2.doOtherStuff();
}
}
For simple cases inheritance is ok as well, but it is not really very flexible.
Implementing multiple interfaces is also a case where this approach scales better.
public interface A{
void doStuff();
}
public interface B{
void doOtherStuff();
}
public class MyStuffDoer implements A, B{
private final A mixin1;
private final B mixin2;
public MyStuffDoer(A mixin1, B mixin2){
this.mixin1 = mixin1;
this.mixin2 = mixin2;
}
public void doStuff(){
mixin1.doStuff();
}
public void doOtherStuff(){
mixin2.doOtherStuff();
}
}
You can't do this with abstract classes. I have used this composition approach on a few projects, and it worked quite fine.