17 Replies Latest reply on Mar 6, 2009 1:47 PM by BKBK

    Cfinterface returntype limitation

    rich.thibault Level 1
      It seems that the returntype validation in cfinterface and implementing classes is a bit too limiting....I can't see how a CFC function could ever return an instance of itself.

      For example, let's say I have an IOrder interface with an init() function where the returntype="IOrder". Then I create an Order.cfc that implements IOrder and add my init() function with returntype="Order". CF blows up at object creation and says the return types do not match. I think this is mistaken, since the Order returntype is in fact an instance of IOrder.

      Is there something I'm missing? I know I can use "any" in both functions as a workaround, but that undermines the whole point of using strict interfaces in the first place.
        • 1. Cfinterface returntype limitation
          -==cfSearching==- Level 4
          Ignoring the question for a moment, why would you want to define a constructor for an interface? Using the java model, interfaces generally define a contract for methods of a class (ie functions of a component), and not constructors. Interfaces are not meant to be instantiated. Though technically the init() function straddles both worlds, it is really more of a constructor than a method/function.

          • 2. Re: Cfinterface returntype limitation
            Level 7
            > It seems that the returntype validation in cfinterface and implementing classes
            > is a bit too limiting....I can't see how a CFC function could ever return an
            > instance of itself.

            This works OK for me.

            <!--- IFoo.cfc --->
            <cfinterface>
            <cffunction name="getSelf" returntype="IFoo">
            </cffunction>
            </cfinterface>

            <!--- Foo.cfc --->
            <cfcomponent implements="shared.cf.cfml.I.interface.IFoo">
            <cffunction name="getSelf" returntype="IFoo">
            <cfreturn this>
            </cffunction>
            </cfcomponent>

            <!--- foo.cfm --->
            <cfset o = createObject("component", "Foo").getSelf()>
            <cfdump var="#o#">


            > Is there something I'm missing?

            I think you're missing the part that if you're having some problems with
            your code, it's always a good idea to post some code that demonstrates the
            problem. Other than that: it's hard to tell.

            --
            Adam
            • 3. Re: Cfinterface returntype limitation
              Level 7
              > why would you want to define a constructor
              > for an interface?

              Because one might want to ensure the class provides a constructor taking a
              specific combination of arguments.

              In the context of CF, there is no concept of constructors, so init() is
              just a method. CF has no way of knowing there's a notional agreement in
              the community that init() methods are generally used in lieu of a
              constructor.

              --
              Adam
              • 4. Re: Cfinterface returntype limitation
                -==cfSearching==- Level 4
                > In the context of CF, there is no concept of constructors, so
                > init() is just a method.

                With regard to CF components, yes.

                > Because one might want to ensure the class provides a constructor
                > taking a specific combination of arguments.

                Yes.. but to me that is more a case for extends versus implements. An interface does not care _how_ a class implements something, only that it does. Constructors are more closely linked to the "how" of the implementation, than methods.

                Having said that, it is possible I just have too much java on the brain ;-) With many of the CF features being modeled after java, it often makes good sense to apply java concepts to CF code as well. Though not always. In some areas CF is more flexible, and it would be silly not take advantage of that.

                Technically, there is nothing syntactically wrong with what you suggested. So perhaps I am just rejecting the break with java tradition here. I will have to give it some more thought :-)



                • 5. Re: Cfinterface returntype limitation
                  Level 7
                  > An
                  > interface does not care _how_ a class implements something, only that it does.
                  > Constructors are more closely linked to the "how" of the implementation, than
                  > methods.

                  Interesting.

                  I've just done some reading and that's certainly the position the Java
                  world takes.

                  I'm not sure I necessarily agree, but I see where they're coming from ;-)

                  --
                  Adam
                  • 6. Cfinterface returntype limitation
                    -==cfSearching==- Level 4
                    > I'm not sure I necessarily agree, but I see where they're coming from ;-)

                    Yes, even after thinking about it I am still in the opposite camp ;-) I would still lean towards using interfaces to group classes/components with similar behaviors, and inheritance for classes/components with similar implementations. For example, you might have different types of Orders that have high level properties in common (like an Order Number), but distinctly different implementations.

                    But, it is not as if using interfaces to enforce a constructor signature is mandatory. So I suppose the world will not end if one does it that way ;-) Though if anyone can think of scenarios where using interfaces would be preferable, I would be interested in hearing it.
                    • 7. Cfinterface returntype limitation
                      BKBK Adobe Community Professional & MVP
                      Rich.Thibault wrote:
                      For example, let's say I have an IOrder interface with an init() function where the returntype="IOrder". Then I create an Order.cfc that implements IOrder and add my init() function with returntype="Order". CF blows up at object creation and says the return types do not match. I think this is mistaken, since the Order returntype is in fact an instance of IOrder.

                      Is there something I'm missing? I know I can use "any" in both functions as a workaround, but that undermines the whole point of using strict interfaces in the first place.


                      I do believe -==cfSearching==- has hit it spot-on. An interface is simply a contract that tells clients** which behaviour(i.e. which methods) a server promises to implement. Since the client knows the interface, it can tell the server to deliver just the behaviour that is exposed in the interface, no more. That makes for clean and efficient coding.

                      As contract, an interface is therefore an empty implementation, and has no business defining a constructor. If it did, that would defeat its very purpose, which is to decouple the client from the server.

                      Since the client depends only on the interface, it is decoupled from the implementation in the component. This reduces dependencies between different parts of your application. You can then vary the implementation within your components at any time without fear of breaking clients.

                      This led to the programming maxim, "Code to the interface, rather than to the implementation". Applying the maxim to your example, would result in something like

                      io = createobject("component", "Order").init(); // io is of type IOrder

                      The init() returns an Order, not an IOrder. See how the lefthand side, io, a static type, refers to a dynamic type at runtime. There lies the power of runtime polymorphism.



                      **By client I mean the object making a request in the form of a method call. By server I mean the object that serves the call, that is, an instance of the component that implements the interface.

                      • 8. Re: Cfinterface returntype limitation
                        rich.thibault Level 1
                        Hi all,

                        Thanks for the responses. I think the init/constructor issue is besides the point though. This issue applies to any function that returns "this".

                        So let's say instead I have a getInstance() function. This works in Java:

                        public interface IMediaExporter {
                        public IMediaExporter getInstance();
                        }

                        public class MediaExporter implements IMediaExporter {
                        public MediaExporter getInstance() {
                        return this;
                        }
                        }

                        This does not work in CF:

                        <cfinterface>
                        <cffunction name="getInstance" output="false" returntype="IMediaExporter">
                        </cffunction>
                        </cfinterface>

                        <cfcomponent output="false" implements="IMediaExporter">
                        <cffunction name="getInstance" output="false" returntype="MediaExporter">
                        <cfscript>
                        return this;
                        </cfscript>
                        </cffunction>
                        </cfcomponent>

                        ERROR: The getInstance function does not specify the same return type in the MediaExporter ColdFusion component and the IMediaExporter ColdFusion interface.

                        Adam, you are correct, I can return the interface type in both the interface and the concrete implementation, but that's not what I want. I want to return a full MediaExporter object, including any additional functions it may include, or even any additional interfaces that it implements. Seems to me since MediaExporter implements IMediaExporter, I should be able to use either one of those as the returntype in MediaExporter.getInstance().
                        • 9. Re: Cfinterface returntype limitation
                          Level 7
                          > Adam, you are correct, I can return the interface type in both the interface
                          > and the concrete implementation, but that's not what I want. I want to return
                          > a full MediaExporter object, including any additional functions it may include,
                          > or even any additional interfaces that it implements. Seems to me since
                          > MediaExporter implements IMediaExporter, I should be able to use either one of
                          > those as the returntype in MediaExporter.getInstance().

                          No, that's not how it works. Just specify the interface name in the return
                          type: that is actually what you want to do here.

                          As you say, a MediaExporter object *is* a specialised type of
                          IMediaExporter, so that's OK.

                          Your method signature in the interface is saying the that the method needs
                          to return an object that is IMediaExporter. It's not saying it
                          specifically needs to be a MediaExporter.

                          Bear in mind that another component might also implement that interface,
                          and it would possibly not be returning a MediaExporter object. Although it
                          could be!

                          The difference being that an IMediaExporter-type object might only defined
                          method1, method2 and method3. A MediaExporter might implement those (well:
                          it would have to in this instance!), but also method4 and method5. Those
                          are two different sets of expectations for the calling code, so you need to
                          be precise about what you tell it to expect. Either a IMediaExporter-type,
                          or a MediaExporter-type.

                          That your implementation of that method is such that it actually
                          specifically returns a MediaExporter is down to the implementation, and
                          beyond the interest of the interface, but it fulfils the requirement, so no
                          problem: there's nothing wrong with that.

                          If you want a method to specifically return a MediaExporter and no other
                          sort of object, it's liekly to be outside the scope of the IMediaExporter
                          interface, because it's too specific. If MediaExporterNew.cfc also
                          implemented IMediaExporter, you'd probably not want its implementation of
                          getInstance() to return a MediaExporter object.

                          Make sense? I'm not sure I'm explaining to as well as one possibly could.

                          --
                          Adam
                          • 10. Re: Cfinterface returntype limitation
                            rich.thibault Level 1
                            Hi Adam, what you say all makes sense, but I still think I should be able to return MediaExporter in the implementing class, just like I can in Java. Since MediaExporter implements IMediaExporter, it should be perfectly legal to substitute it there as the returntype. It's not a mismatch at all, as the error message indicates, IMHO.
                            • 11. Re: Cfinterface returntype limitation
                              Level 7
                              > Hi Adam, what you say all makes sense, but I still think I should be able to
                              > return MediaExporter in the implementing class, just like I can in Java.

                              Crikey dick, so you can! I didn't know that. I didn't believe you so I
                              knocked together a test class and lo and behold. I'm not sure why I didn't
                              come across that before. I'm by no means a Java expert (I'm very much a
                              Java novice), but I'm sure I would have recalled that from my travails had
                              any of my books and what not pointed it out. Unfortunately my Head FIrst
                              Java book is in a box in NZ and I'm in the UK, so I can't be checking.

                              OK, so my wee story before was woven on the basis of how CF works, and just
                              guessing how it ought to work elsewhere. I take a small amount of solace
                              from the fact (although I'm not sure) that C# doesn't seem to allow it.
                              Well my 10min foray into experimentation failed, anyhow.


                              > Since
                              > MediaExporter implements IMediaExporter, it should be perfectly legal to
                              > substitute it there as the returntype. It's not a mismatch at all, as the
                              > error message indicates, IMHO.

                              Yep, sure. I agree with you now (note to others around here: see it does
                              happen sometimes ;-).

                              I wonder whether CF's way of doing it is a bug, or by design or some side
                              effect of typelessness? I'm suspecting the first one. I shall do some
                              more experimentation and ask around.

                              Cheers for the heads-up: I like being proven wrong because it means I learn
                              something new ;-)

                              --
                              Adam
                              • 12. Re: Cfinterface returntype limitation
                                -==cfSearching==- Level 4
                                > I still think I should be able to return MediaExporter in the implementing class, just like I can in Java.

                                In later versions, yes.

                                > I wonder whether CF's way of doing it is a bug, or by design or some side
                                > effect of typelessness? I'm suspecting the first one. I shall do some
                                > more experimentation and ask around.

                                Since CF does not support overloading either, I would not be very surprised if it did not support covariant return types, by design. Personal preferences aside, it does mention in the documentation "Must be identical to value in interface; however, an omitted type option and an option value of any are equivalent". That certainly does read as if it was a conscious decision. Though the fact that you can also return "any" muddies the waters a bit.
                                (But that might just be a necessity due to the typelessness factor.)

                                Bottom line, I do not know the true "why" of it either. While I have my suspicions, I would be very interested in hearing a more definitive answer.

                                > I take a small amount of solace
                                > from the fact (although I'm not sure) that C# doesn't seem to allow it.
                                > Well my 10min foray into experimentation failed, anyhow.

                                You are right. I did not know C# does does not support them either. So cheers for that.

                                > I agree with you now (note to others around here: see it does
                                > happen sometimes ;-).

                                Phew! That explains the flying pigs I saw out my window ;-)

                                (Just kidding)
                                • 13. Cfinterface returntype limitation
                                  BKBK Adobe Community Professional & MVP
                                  Rich.thibault wrote:
                                  public interface IMediaExporter {
                                  public IMediaExporter getInstance();
                                  }

                                  public class MediaExporter implements IMediaExporter {
                                  public MediaExporter getInstance() {
                                  return this;
                                  }
                                  }

                                  This does not work in CF:

                                  <cfinterface>
                                  <cffunction name="getInstance" output="false" returntype="IMediaExporter">
                                  </cffunction>
                                  </cfinterface>

                                  <cfcomponent output="false" implements="IMediaExporter">*
                                  <cffunction name="getInstance" output="false" returntype="MediaExporter">*
                                  <cfscript>
                                  return this;
                                  </cfscript>
                                  </cffunction>
                                  </cfcomponent>


                                  -==cfSearching==- wrote:
                                  Since CF does not support overloading either, I would not be very surprised if it did not support covariant return types, by design. Personal preferences aside, it does mention in the documentation "Must be identical to value in interface; however, an omitted type option and an option value of any are equivalent". That certainly does read as if it was a conscious decision. Though the fact that you can also return "any" muddies the waters a bit.

                                  I suspect it's got to do with the fact that Java's concept of static type-checking doesn't (yet) carry over to Coldfusion. I am referring to the meaning static that is opposed to dynamic or runtime.

                                  The static type is the type of the reference. Java checks the static type at compile-time. To borrow from your example, one may write in Java

                                  IMediaExporter myMediaExporter = new MediaExporter();

                                  Here, the static type, that is, the type of the reference, is the interface, IMediaExporter. There is no equivalent to that line of code in Coldfusion components. Coldfusion has (as yet) no static-type checking. I guess that that is the reason behind the requirement that the return types be identical or else "any".

                                  However, all is not lost. Modify your code as follows.

                                  <cfinterface>
                                  <cffunction name="getInstance" output="false" returntype="IMediaExporter">*
                                  </cffunction>
                                  </cfinterface>

                                  <cfcomponent output="false" implements="IMediaExporter">
                                  <cffunction name="getInstance" output="false" returntype="IMediaExporter">*
                                  <cfscript>
                                  return this;
                                  </cfscript>
                                  </cffunction>
                                  </cfcomponent>

                                  <!--- IMediaExporter type --->
                                  <cfset iObj = createobject("component", "MediaExporter").getInstance()>

                                  <p>Is returned IMediaExporter type an instance of MediaExporter: <cfoutput>#isInstanceOf(iObj,'MediaExporter')#</cfoutput></p>

                                  Coldfusion would tell you that the returned IMediaExporter type is an instance of MediaExporter. Also, there is always the possibility to leave everything unchanged in your Java code, and to do this:

                                  <cfset obj=createobject("java","MediaExporter")>
                                  <cfset iObj=createobject("java","IMediaExporter")>



                                  * Correction following feedback from Rich-thibault and -==cfSearching==-. The type returned should indeed be the interface IMediaExporter , not the component MediaExporter. There is no such thing as an instantiation of the interface, as I, myself, pointed out. (NB: I interprete Coldfusion's isInstanceOf to mean, in the case of interfaces, typing rather than instantiation)



                                  • 14. Re: Cfinterface returntype limitation
                                    rich.thibault Level 1
                                    Hi BKBK,

                                    The only problem with your approach - putting the concrete class as the return type in the interface - is that there may (and should!) be multiple implementations tied to the same interface, and the interface doesn't (and shouldn't!) have any knowledge of them. It doesn't know whether to return a MediaExporter, a VideoMediaExporter, an AudioMediaExporter, a PurpleBananaMediaExporter, etc.

                                    I'm actually leaning more towards Adam's suggestion of returning IMediaExporter in both places:

                                    <cfinterface>
                                    <cffunction name="getInstance" output="false" returntype="IMediaExporter">
                                    </cffunction>
                                    </cfinterface>

                                    <cfcomponent output="false" implements="IMediaExporter">
                                    <cffunction name="getInstance" output="false" returntype="IMediaExporter">
                                    <cfscript>
                                    return this;
                                    </cfscript>
                                    </cffunction>
                                    </cfcomponent>

                                    This feels kinda wrong to me, and yet examining MediaExporter objects returned by getInstance() shows them to still be instances of MediaExporter, and all functions are available, even those not mandated by the IMediaExporter interface. I haven't played around with it enough to know if there are any issues when MediaExporter implements multiple interfaces, but I imagine there aren't.

                                    • 15. Re: Cfinterface returntype limitation
                                      -==cfSearching==- Level 4
                                      rich.thibault wrote:
                                      > The only problem with your approach - putting the concrete class as the return type in
                                      > the interface - is that there may (and should!) be multiple implementations tied to the same
                                      > interface, and the interface doesn't (and shouldn't!) have any knowledge of them.

                                      Yes, I have to agree. Returning a concrete class, to get around the apparent limitation of cfinterface, sort of defeats the purpose of interfaces. It would be nice if cfinterface worked the way interfaces in java 1.5+ do, but apparently they do not right now.

                                      > I'm actually leaning more towards Adam's suggestion of returning
                                      > IMediaExporter in both places:

                                      Based on the documentation, that seems like exactly what you must do. The only other option is to return type "any".

                                      > This feels kinda wrong to me, and yet examining MediaExporter objects returned
                                      > by getInstance() shows them to still be instances of MediaExporter, and all
                                      > functions are available, even those not mandated by the IMediaExporter interface.

                                      Yes, I can see why it feels wrong. But as long as the functions are available, and you can call them, it does not seem like a problem. It is not as if you have to cast them to a different type, as you might in java.
                                      • 16. Re: Cfinterface returntype limitation
                                        BKBK Adobe Community Professional & MVP
                                        Rich.thibault and -==cfSearching==-, you are right, and I am wrong. The example I gave contradicts everything I had just said. I have corrected it accordingly. Thanks.



                                        • 17. Re: Cfinterface returntype limitation
                                          BKBK Adobe Community Professional & MVP
                                          Rich.thibault wrote:
                                          This feels kinda wrong to me, and yet examining MediaExporter objects returned by getInstance() shows them to still be instances of MediaExporter, and all functions are available, even those not mandated by the IMediaExporter interface.

                                          I think it's runtime polymorphism in action. One reference, many forms. At runtime, the returned this is at once of type IMediaExporter and an instance of MediaExporter. For example, the following test will display

                                          string zero
                                          string one
                                          string two

                                          even though the functions f and f2 aren't defined in the interface IExample1.