This pattern can be used to good effect, but this doesn't look like an example. A good example would be to communicate semantic constraints/guarantees that don't otherwise change the API. For example, you could have an interface that represents a kind of operation, and an a sub-interface that indicates that the operation is idempotent. Then then returning an
IIdempotentOperation indicates (intended) guarantees to the consumer, and receiving an
IIdempotentOperation indicates additional constraints to the user.
But you have to have something to talk about. If the interface doesn't have (directly or indirectly) any methods, then either it's saying something about the methods on all objects, e.g.
Equals, or it is saying that the object is one of a fixed, known set of classes for the purposes of type-casing via
instanceof or similar. Maybe that is the idea, but then that's pretty ugly. If it isn't referencing a situation like either of the above, then there is absolutely no reason for the parameters to not be completely parametric, and it says nothing to implement such an interface.
I should clarify that it's
ITaskId that seems completely useless to me. I can see what
ITask<> is trying to accomplish. More context would be needed to tell if those interfaces are adding value. Excepting the scenarios I described above, there's literally no difference between
class Foo and
class Foo : ITaskId. Similarly, having a parameter of a method of type
ITaskId is no different from having a parameter of type
Object (or, preferably, abstracting it into a generic type parameter). There's simply no semantic constraint that's even informally expressible because there are no methods to constrain. Again, unless it's informally indicating a constraint on a method like
ToString, there's simply no behavior for a constraint to constrain. I would even say that it is not just unnecessary to have the
ITaskId constraint e.g. on
TId, but it's actually a mistake. You can always constrain the
TId type parameter in any particular use if necessary, but it may be useful to allow
TId to be types that do not implement
ITaskId, such as
String, even if only during an intermediate calculation. The Haskell community has run into the issues of unnecessary constraints limiting flexibility in painful ways with the most notable example being the
Complex type which used to be constrained but now is not (and in fact the language feature allowing constraints in such positions has been removed as they were always unnecessary).