Default Interface Methods. What is deep meaningful difference now, between abstract class and interface?
There is not a lot of difference between the two apart from the obvious fact that abstract classes can have state and interfaces cannot. Default methods or also known as virtual extension methods have actually been available in Java for a while. The main drive for default methods is interface evolution which means being able to add methods to an interface in future versions without breaking source or binary compatibility with existing implementations of that interface.
another couple of good points mentioned by this post:
- The feature enables C# to interoperate with APIs targeting Android (Java) and iOs (Swift), which support similar features.
- As it turns out, adding default interface implementations provides the elements of the "traits" language feature (https://en.wikipedia.org/wiki/Trait_(computer_programming)). Traits have proven to be a powerful programming technique (http://scg.unibe.ch/archive/papers/Scha03aTraits.pdf).
Conceptual
First of all, there is a conceptual difference between a class and an interface.
- A class should describe an "is a" relationship. E.g. a Ferrari is a Car
- An interface should describe a contract of a type. E.g. A Car has a steering wheel.
Currently abstract classes are sometimes used for code reuse, even when there is no "is a" relationship. This pollutes the OO design. E.g. FerrariClass
inherits from CarWithSteeringWheel
Benefits
- So from above, you could reuse code without introducing a (conceptually wrong) abstract class.
- You could inherit from multiple interfaces, while an abstract class is only single inheritance
- There is co- and contravariance on interfaces and not on classes in C#
- It's easier to implement an interface because some methods have default implementations. This could save a lot of work for an implementer of the interface, but the user won't see the difference :)
- But most important for me (as I'm a library maintainer), you could add new methods to an interface without making a breaking change! Before C# 8, if an interface was publicly published, it should be fixed. Because changing the interface could break a lot.
The logger interface
This example shows some of the benefits.
You could describe a (oversimplified) logger interface as follows:
interface ILogger
{
void LogWarning(string message);
void LogError(string message);
void Log(LogLevel level, string message);
}
Then a user of that interface could log easily as warning and error using LogWarning
and LogError
. But the downside is that an implementer must implement all the methods.
An better interface with defaults would be:
interface ILogger
{
void LogWarning(string message) => Log(LogLevel.Warning, message);
void LogError(string message) => Log(LogLevel.Error, message);
void Log(LogLevel level, string message);
}
Now a user could still use all the methods, but the implementer only needs to implement Log
. Also, he could implement LogWarning
and LogError
.
Also, in the future you might like to add the logLevel "Catastrophic". Before C#8 you could not add the method LogCatastrophic
to ILogger without breaking all current implementations.
Another thing which still makes the interface unique is covariance / contravariance.
To be honest, never found myself in situation where a default impl. in interface was the solution. I am a bit sceptical about it.