Why is this legal?

via Digitalmars-d-learn digitalmars-d-learn at puremagic.com
Wed Mar 29 04:06:55 PDT 2017


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


More information about the Digitalmars-d-learn mailing list