www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Abstract Classes vs Interfaces

reply Justin Johansson <no spam.com> writes:
Currently I'm struggling with unifying some C++ and Java code with
the idea that I might eventually port it to D2.

C++ people will know that their language supports abstract classes
and multiple inheritance but not interfaces per se (although they
can be hacked as abstract classes with implicit/trivial constructors).

Java people will know that their language also supports abstract
classes and interfaces though not multiple inheritance.

In respect of the support of abstract classes, multiple inheritance
and interfaces, both D1 and D2 are closer to Java than C++.

May I please ask of this group their opinions as to the difference
between abstract classes and interfaces from an axiomatic viewpoint.

Is there a difference axiomatically, semantically or otherwise?

Thanks in advance for all comments,

Justin Johansson
Jul 07 2010
next sibling parent reply "Robert Jacques" <sandford jhu.edu> writes:
On Wed, 07 Jul 2010 07:24:15 -0400, Justin Johansson <no spam.com> wrote:

 Currently I'm struggling with unifying some C++ and Java code with
 the idea that I might eventually port it to D2.

 C++ people will know that their language supports abstract classes
 and multiple inheritance but not interfaces per se (although they
 can be hacked as abstract classes with implicit/trivial constructors).

 Java people will know that their language also supports abstract
 classes and interfaces though not multiple inheritance.

 In respect of the support of abstract classes, multiple inheritance
 and interfaces, both D1 and D2 are closer to Java than C++.

 May I please ask of this group their opinions as to the difference
 between abstract classes and interfaces from an axiomatic viewpoint.

 Is there a difference axiomatically, semantically or otherwise?

 Thanks in advance for all comments,

 Justin Johansson

Axiomatically, an interface can be thought of an abstract class without any field or method definitions. In D2, interfaces now support contracts and static/final methods. The real issues is that there are a host of well known multiple-inheritance problems with abstract classes once you move beyond virtual function (i.e. beyond interfaces). For example, It seems that D2's interface functions have inherited some of these issues. Consider the following: interface A { final void foobar() { writeln("A"); } } interface B { final void foobar() { writeln("B"); } } class C : A, B {} void main(string[] args) { C c = new C; c.foobar; } What does foobar print? (This has been filed as bug 4435: http://d.puremagic.com/issues/show_bug.cgi?id=4435)
Jul 07 2010
next sibling parent Jonathan M Davis <jmdavisprog gmail.com> writes:
 Consider the following:
 
 interface A { final void foobar() { writeln("A"); } }
 interface B { final void foobar() { writeln("B"); } }
 
 class C : A, B {}
 
 void main(string[] args) {
 
      C c = new C;
      c.foobar;
 
 }
 
 What does foobar print?
 (This has been filed as bug 4435:
 http://d.puremagic.com/issues/show_bug.cgi?id=4435)

I believe that per TDPL, you would have to call these with eithe c.A.foobar() or c.B.foobar() but that c.foobar() is not allowed. If it is allowed at present, it's definitely a bug. However, it's a bug in the implementation rather than the language spec. - Jonathan M Davis
Jul 07 2010
prev sibling next sibling parent "Robert Jacques" <sandford jhu.edu> writes:
On Wed, 07 Jul 2010 13:36:16 -0400, Jonathan M Davis  
<jmdavisprog gmail.com> wrote:

 Consider the following:

 interface A { final void foobar() { writeln("A"); } }
 interface B { final void foobar() { writeln("B"); } }

 class C : A, B {}

 void main(string[] args) {

      C c = new C;
      c.foobar;

 }

 What does foobar print?
 (This has been filed as bug 4435:
 http://d.puremagic.com/issues/show_bug.cgi?id=4435)

I believe that per TDPL, you would have to call these with eithe c.A.foobar() or c.B.foobar() but that c.foobar() is not allowed. If it is allowed at present, it's definitely a bug. However, it's a bug in the implementation rather than the language spec. - Jonathan M Davis

TDPL Page 215-216, mentions function hijacking of a final function by the implementor, which is listed as an error and detected correctly. Function hijacking of static functions by the implementor is not handled correctly. It also specifically mentions that c.A.foobar() is not the correct, nor ideal syntax. Indeed, it doesn't currently compile.
Jul 07 2010
prev sibling parent "Robert Jacques" <sandford jhu.edu> writes:
On Wed, 07 Jul 2010 14:38:09 -0400, Robert Jacques <sandford jhu.edu>  
wrote:
 On Wed, 07 Jul 2010 13:36:16 -0400, Jonathan M Davis  
 <jmdavisprog gmail.com> wrote:

 Consider the following:

 interface A { final void foobar() { writeln("A"); } }
 interface B { final void foobar() { writeln("B"); } }

 class C : A, B {}

 void main(string[] args) {

      C c = new C;
      c.foobar;

 }

 What does foobar print?
 (This has been filed as bug 4435:
 http://d.puremagic.com/issues/show_bug.cgi?id=4435)

I believe that per TDPL, you would have to call these with eithe c.A.foobar() or c.B.foobar() but that c.foobar() is not allowed. If it is allowed at present, it's definitely a bug. However, it's a bug in the implementation rather than the language spec. - Jonathan M Davis

TDPL Page 215-216, mentions function hijacking of a final function by the implementor, which is listed as an error and detected correctly. Function hijacking of static functions by the implementor is not handled correctly. It also specifically mentions that c.A.foobar() is not the correct, nor ideal syntax. Indeed, it doesn't currently compile.

Since I forgot to mention it, c.foobar is the correct syntax.
Jul 07 2010
prev sibling next sibling parent Jonathan M Davis <jmdavisprog gmail.com> writes:
On Wednesday, July 07, 2010 11:38:09 Robert Jacques wrote:
 On Wed, 07 Jul 2010 13:36:16 -0400, Jonathan M Davis
 
 <jmdavisprog gmail.com> wrote:
 Consider the following:
 
 interface A { final void foobar() { writeln("A"); } }
 interface B { final void foobar() { writeln("B"); } }
 
 class C : A, B {}
 
 void main(string[] args) {
 
      C c = new C;
      c.foobar;
 
 }
 
 What does foobar print?
 (This has been filed as bug 4435:
 http://d.puremagic.com/issues/show_bug.cgi?id=4435)

I believe that per TDPL, you would have to call these with eithe c.A.foobar() or c.B.foobar() but that c.foobar() is not allowed. If it is allowed at present, it's definitely a bug. However, it's a bug in the implementation rather than the language spec. - Jonathan M Davis

TDPL Page 215-216, mentions function hijacking of a final function by the implementor, which is listed as an error and detected correctly. Function hijacking of static functions by the implementor is not handled correctly. It also specifically mentions that c.A.foobar() is not the correct, nor ideal syntax. Indeed, it doesn't currently compile.

That's talking about private final functions in an interface. Read section 6.9.3 on pages 217 - 218. 1. If you have a method x() which is in two different interfaces and non-final in both, then when a class implements both interfaces, it's single method x() implements both interfaces x() method simultaneously. interface A { void x(); } interface B { void x(); } class C : A, B { void x() { /* implements both */ } } void main() { C c = new C(); c.x(); // called like this } 2. If you have a method x() which is two different interfaces and final in both, then you can't implement x() in a class which implements both interfaces. You also can't call it like c.x() because the compiler wouldn't know which to call. You have to specify which interface it's being called for. interface A { final void x() { /*do something*/ } interface B { final void x() { /*do something else*/; } class C : A, B { /* cannot implement x()*/ } void main() { C c = new C(); c.A.x(); // called like this to get A's version c.B.x(); // called like this to get B's version } 3. The third case is when the method is final in one interface and not in the other. TDPL doesn't say what happens in this case. C wouldn't be able to implement x() because it would be final in one of the interfaces, but I'm not sure if that means the other interface's x() would be used in that case or if you couldn't implement both interfaces together. My guess is the latter, but TDPL doesn't say. interface A { final void x() { /*do something*/ } interface B { void x(); } class C : A, B { /* Is it legal to implement both interfaces in this case? */ } I don't know what exactly dmd does in all cases at this point, but TDPL is quite clear that if a class implements two interfaces that both have a final method with the same name, then if you're calling that method using the class, you have to specify which interface's method you're calling. - Jonathan M Davis
Jul 07 2010
prev sibling next sibling parent "Robert Jacques" <sandford jhu.edu> writes:
On Wed, 07 Jul 2010 17:07:39 -0400, Jonathan M Davis  
<jmdavisprog gmail.com> wrote:

 On Wednesday, July 07, 2010 11:38:09 Robert Jacques wrote:
 On Wed, 07 Jul 2010 13:36:16 -0400, Jonathan M Davis

 <jmdavisprog gmail.com> wrote:
 Consider the following:

 interface A { final void foobar() { writeln("A"); } }
 interface B { final void foobar() { writeln("B"); } }

 class C : A, B {}

 void main(string[] args) {

      C c = new C;
      c.foobar;

 }

 What does foobar print?
 (This has been filed as bug 4435:
 http://d.puremagic.com/issues/show_bug.cgi?id=4435)

I believe that per TDPL, you would have to call these with eithe c.A.foobar() or c.B.foobar() but that c.foobar() is not allowed. If it is allowed at present, it's definitely a bug. However, it's a bug in the implementation

 than the
 language spec.

 - Jonathan M Davis

TDPL Page 215-216, mentions function hijacking of a final function by the implementor, which is listed as an error and detected correctly. Function hijacking of static functions by the implementor is not handled correctly. It also specifically mentions that c.A.foobar() is not the correct, nor ideal syntax. Indeed, it doesn't currently compile.

That's talking about private final functions in an interface. Read section 6.9.3 on pages 217 - 218. 1. If you have a method x() which is in two different interfaces and non-final in both, then when a class implements both interfaces, it's single method x() implements both interfaces x() method simultaneously. interface A { void x(); } interface B { void x(); } class C : A, B { void x() { /* implements both */ } } void main() { C c = new C(); c.x(); // called like this } 2. If you have a method x() which is two different interfaces and final in both, then you can't implement x() in a class which implements both interfaces. You also can't call it like c.x() because the compiler wouldn't know which to call. You have to specify which interface it's being called for. interface A { final void x() { /*do something*/ } interface B { final void x() { /*do something else*/; } class C : A, B { /* cannot implement x()*/ } void main() { C c = new C(); c.A.x(); // called like this to get A's version c.B.x(); // called like this to get B's version } 3. The third case is when the method is final in one interface and not in the other. TDPL doesn't say what happens in this case. C wouldn't be able to implement x() because it would be final in one of the interfaces, but I'm not sure if that means the other interface's x() would be used in that case or if you couldn't implement both interfaces together. My guess is the latter, but TDPL doesn't say. interface A { final void x() { /*do something*/ } interface B { void x(); } class C : A, B { /* Is it legal to implement both interfaces in this case? */ } I don't know what exactly dmd does in all cases at this point, but TDPL is quite clear that if a class implements two interfaces that both have a final method with the same name, then if you're calling that method using the class, you have to specify which interface's method you're calling. - Jonathan M Davis

Thanks for the page numbers. (And I'm suddenly lamenting my slow reading of TDPL). Anyways, I think the above makes perfect sense, but none of it is implemented in DMD at this point.
Jul 07 2010
prev sibling parent reply Ary Borenszweig <ary esperanto.org.ar> writes:
On 07/07/2010 06:24 PM, Justin Johansson wrote:
 Currently I'm struggling with unifying some C++ and Java code with
 the idea that I might eventually port it to D2.

 C++ people will know that their language supports abstract classes
 and multiple inheritance but not interfaces per se (although they
 can be hacked as abstract classes with implicit/trivial constructors).

 Java people will know that their language also supports abstract
 classes and interfaces though not multiple inheritance.

 In respect of the support of abstract classes, multiple inheritance
 and interfaces, both D1 and D2 are closer to Java than C++.

 May I please ask of this group their opinions as to the difference
 between abstract classes and interfaces from an axiomatic viewpoint.

 Is there a difference axiomatically, semantically or otherwise?

 Thanks in advance for all comments,

 Justin Johansson

Basically an interface is a contract: if a class implement that interface then it provides all the methods described in the interface. An abstract class just serves building common functionality for a set of classes that will inherit from it. If an abstract class provides no functionality at all then it might well be think of as an interface (it should be replaced by an interface.) From the other reply it seems that interfaces in D can have final/static methods. That kind of goes against what I just said about interfaces...
Jul 08 2010
next sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Thu, 08 Jul 2010 07:59:08 -0400, Ary Borenszweig <ary esperanto.org.ar>  
wrote:

 On 07/07/2010 06:24 PM, Justin Johansson wrote:
 Currently I'm struggling with unifying some C++ and Java code with
 the idea that I might eventually port it to D2.

 C++ people will know that their language supports abstract classes
 and multiple inheritance but not interfaces per se (although they
 can be hacked as abstract classes with implicit/trivial constructors).

 Java people will know that their language also supports abstract
 classes and interfaces though not multiple inheritance.

 In respect of the support of abstract classes, multiple inheritance
 and interfaces, both D1 and D2 are closer to Java than C++.

 May I please ask of this group their opinions as to the difference
 between abstract classes and interfaces from an axiomatic viewpoint.

 Is there a difference axiomatically, semantically or otherwise?

 Thanks in advance for all comments,

 Justin Johansson

Basically an interface is a contract: if a class implement that interface then it provides all the methods described in the interface. An abstract class just serves building common functionality for a set of classes that will inherit from it. If an abstract class provides no functionality at all then it might well be think of as an interface (it should be replaced by an interface.) From the other reply it seems that interfaces in D can have final/static methods. That kind of goes against what I just said about interfaces...

Not really, the class still does implement the required functions. It's just that when some functions *always* are the same, then a final method makes it easier to maintain that one method. In java/C# you have this awkward construct: interface I { int foo(int x); int bar(); // Note, implementing classes should *always* implement this as foo(bar()); int foo(); } Whereas in D, you have: interface I { int foo(int x); int bar(); final int foo() { return foo(bar()); } } Much more elegant if you ask me... And to clarify, the difference between an abstract class and an interface is not so much the function implementation, it's that an abstract class can have data members and an interface cannot. The difficulty in implementing multiple inheritance is directly related to data members. The problem is, class inheritance is based on the axiom that a pointer to the base class is the same as a pointer to the derived class. This allows all kinds of cool things to work with very little performance problems. However, if you have two base classes, then you have two *different* base class pointers, since the pointer to the base class must look like a pointer to an instance of the base class when calling the base class' methods. This means casts to base classes involve changing pointers, and you can't simply call base functions without adding some sort of offset. It also makes casting to derived classes much more complex and error prone. It also introduces the diamond problem (where you inherit from two base classes that themselves have the same base class, giving you two copies of the lower base class) and the horrible solution, virtual inheritance. Also, multiple inheritance can be done, quite easily in D, with the new alias this construct. It exposes some of the ugliness of multiple inheritance, but I think that's to the programmer's benefit. -Steve
Jul 08 2010
prev sibling parent Justin Johansson <no spam.com> writes:
Ary Borenszweig wrote:
 On 07/07/2010 06:24 PM, Justin Johansson wrote:
 Currently I'm struggling with unifying some C++ and Java code with
 the idea that I might eventually port it to D2.

 C++ people will know that their language supports abstract classes
 and multiple inheritance but not interfaces per se (although they
 can be hacked as abstract classes with implicit/trivial constructors).

 Java people will know that their language also supports abstract
 classes and interfaces though not multiple inheritance.

 In respect of the support of abstract classes, multiple inheritance
 and interfaces, both D1 and D2 are closer to Java than C++.

 May I please ask of this group their opinions as to the difference
 between abstract classes and interfaces from an axiomatic viewpoint.

 Is there a difference axiomatically, semantically or otherwise?

 Thanks in advance for all comments,

 Justin Johansson


First, thanks to all respondents.
 Basically an interface is a contract: if a class implement that 
 interface then it provides all the methods described in the interface.

It's not that I don't understand interfaces and abstract classes; just that I felt there was a underlying idea about some fundamental concept that has hitherto been eluded. Now I think that there is nothing else other than that the set of all abstract classes includes the set of all interfaces and that about it. Abstract classes and interfaces are simply variants of a tool for defining contractual call interfaces. Different languages give you different features as to exactly what you can do and cannot do with these tools.
 An abstract class just serves building common functionality for a set of 
 classes that will inherit from it. If an abstract class provides no 
 functionality at all then it might well be think of as an interface (it 
 should be replaced by an interface.)

Good point you ended with there. Yes, I agree that the least featured tool should be used when there is a choice of tools which are all capable of doing the job, all other things being equal.
  From the other reply it seems that interfaces in D can have 
 final/static methods. That kind of goes against what I just said about 
 interfaces...

Yes, well, D provides implementation of these tools that might be a little different to the concepts of these tools in other languages. It's the abstract concept of abstract classes and interfaces which was my main motivation in posting the question. I think, though, from what you have said, abstract classes are interfaces on steroids. Worth mentioning though, that with the exception of multiple inheritance alla C++), abstract classes in languages with single inheritance models cannot declare classes which inherit from multiple abstract classes Naturally, there's something tautological about this last point! Cheers Justin Johansson
Jul 08 2010