On Wednesday, 29 March 2017 at 10:12:08 UTC, abad wrote:
On Wednesday, 29 March 2017 at 10:08:02 UTC, abad wrote:
Related question, it seems that final methods are allowed in
interfaces. Obviously you can't implement them anywhere, so is
this also on purpose and on what rationale? :)
So actually it's just a question of not catching this mistake
early, because obviously compilation will fail when any class
tries to implement the interface so the end result is ok.
Maybe it _could_ just disallow final methods altogether to
catch the errors earlier. But very minor detail overall.
The idea between `final` functions in interfaces is to provide a
default non-overridable implementation. For example:
interface Lockable
{
void lock();
void unlock();
alias Action = void delegate();
final void performLocked(Action action)
{
lock();
// Ensures that the lock will be released after `action`
// is called, even if throws an exception.
scope(exit) unlock();
action();
}
}
class Mutex : Lockable
{
void lock() { /* ... */ }
void unlock() { /* ... */ }
// Can't override `performLocked` differently
}
A common example is frameworks which provide customization points
for applications through non-final interface functions, but
overall take-over the application control flow:
interface App
{
/// Main application loop
final bool run()
{
init();
while(handleInput())
{
update();
auto frame = render();
// implement somewhere else as a free function
present(frame);
}
return true;
}
/// Initializes the application's resources on startup.
void init();
/// Handles the input.
/// Returns:
/// false - if the app should be closed and true -
otherwise.
bool handleInput();
/// Updates the application state after handling input.
void update();
/// Renders and the next frame into a buffer and
/// returns a reference to it.
Framerender();
}
Other times, it's just for convenience in generic code:
interface SceneDscNode
{
final T get(T)() const
{
static if (isBoolean!T) return getBool();
else static if (isIntegral!T) return getInt.to!T();
else static if (isFloatingPoint!T) return getFloat.to!T();
else static if (isSomeString!T) return getString.to!T();
else static assert(0, "Type not supported: " ~
T.stringof);
}
string getName() const;
SceneDscNode getChild(string propertyName) const;
SceneDscNode[] getChildren() const;
protected:
bool getBool() const;
long getInt() const;
double getFloat() const;
string getString() const;
}
class JsonValueWrapper : SceneDscNode
{
string getName() const { /* ... */ }
SceneDscNode getChild(string propertyName) const { /* ... */ }
SceneDscNode[] getChildren() const { /* ... */ }
protected
{
bool getBool() const { /* ... */ }
long getInt() const { /* ... */ }
double getFloat() const { /* ... */ }
string getString() const { /* ... */ }
}
private JSONValue json;
}
class SdlValueWrapper : SceneDscNode
{
string getName() const { /* ... */ }
SceneDscNode getChild(string propertyName) const { /* ... */ }
SceneDscNode[] getChildren() const { /* ... */ }
protected
{
bool getBool() const { /* ... */ }
long getInt() const { /* ... */ }
double getFloat() const { /* ... */ }
string getString() const { /* ... */ }
}
private const SDLTag sdl;
}