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;
}

Reply via email to