Если у вас есть следующий шаблон, вы найдете его наиболее гибким:
interface Fooable
{
void foo();
void bar();
}
abstract class AbstractFoo
implements Fooable
{
}
class Foo
extends AbstractFoo
{
public void foo()
{
}
public void bar()
{
}
}
Таким образом, вы всегда можете пройти через интерфейс, но если позже вы обнаружите, что у вас есть общий код, вы можете поместить его в абстрактный класс, не внося изменений во все классы.
Если у вас нет веских причин не использовать этот шаблон (и я подозреваю, что вы этого не делаете), я бы предложил использовать его. Это может привести к пустым абстрактным классам, но я думаю, что это нормально (немного странно, но хорошо).
Если в интерфейсе действительно нет методов или только один, я бы пропустил абстрактный класс.
С точки зрения компилятора / функциональности нет реальной разницы между интерфейсом и абстрактным классом, где все методы являются абстрактными. Интерфейс будет более гибким, чем абстрактный класс, а интерфейс + абстрактный класс будет наиболее гибким из всех.
Если бы это был мой код, я бы сделал так, чтобы они были интерфейсами ...