• Global community
    • Language:
      • Deutsch
      • English
      • Español
      • Français
      • Português
  • 日本語コミュニティ
    Dedicated community for Japanese speakers
  • 한국 커뮤니티
    Dedicated community for Korean speakers
Exit
0

Cfinterface returntype limitation

Explorer ,
Feb 27, 2009 Feb 27, 2009

Copy link to clipboard

Copied

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.
TOPICS
Advanced techniques

Views

1.3K

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Valorous Hero ,
Feb 27, 2009 Feb 27, 2009

Copy link to clipboard

Copied

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.

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Mar 06, 2009 Mar 06, 2009

Copy link to clipboard

Copied

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.



Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
LEGEND ,
Feb 28, 2009 Feb 28, 2009

Copy link to clipboard

Copied

> 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

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
LEGEND ,
Feb 28, 2009 Feb 28, 2009

Copy link to clipboard

Copied

> 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

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Valorous Hero ,
Feb 28, 2009 Feb 28, 2009

Copy link to clipboard

Copied

> 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 :-)



Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
LEGEND ,
Feb 28, 2009 Feb 28, 2009

Copy link to clipboard

Copied

> 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

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Valorous Hero ,
Mar 01, 2009 Mar 01, 2009

Copy link to clipboard

Copied

> 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.

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Mar 01, 2009 Mar 01, 2009

Copy link to clipboard

Copied

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.

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Explorer ,
Mar 02, 2009 Mar 02, 2009

Copy link to clipboard

Copied

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().

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
LEGEND ,
Mar 02, 2009 Mar 02, 2009

Copy link to clipboard

Copied

> 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

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Explorer ,
Mar 02, 2009 Mar 02, 2009

Copy link to clipboard

Copied

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.

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
LEGEND ,
Mar 02, 2009 Mar 02, 2009

Copy link to clipboard

Copied

> 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

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Valorous Hero ,
Mar 02, 2009 Mar 02, 2009

Copy link to clipboard

Copied

> 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)

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Explorer ,
Mar 06, 2009 Mar 06, 2009

Copy link to clipboard

Copied

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.

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Valorous Hero ,
Mar 06, 2009 Mar 06, 2009

Copy link to clipboard

Copied

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.

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Mar 05, 2009 Mar 05, 2009

Copy link to clipboard

Copied

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)



Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Mar 06, 2009 Mar 06, 2009

Copy link to clipboard

Copied

LATEST
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.

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Resources
Documentation