I guess I need to go back and have a decent look at abstract components and methods at some stage, after my abortive first attempt ("Lucee 5 beta: abstract components (
If one flags a component or method as final, then the intent is that that component or method cannot be extended by a sub component (or a method in same). This is a common feature in OO languages, although was never initially implemented in CFML's OO implementation. I really question the merits of adding it to CFML now, really. Beyond the idea that "well it should have been there", it's kind of only relevant for people building APIs, which doesn't often happen in CFML, and I've always found usage of final to be a pain in the arse more than anything actually useful. My position is I should be able to extend any class I like: it's not for the author of a class to pre-decide how I might end up using it.
Anyway, Lucee has added this in. I guess it makes sense if they've added in abstract classes and methods (another questionable, but slightly more useful, addition IMO).
Here are the relevant examples:
// FinalComponent.cfc
final component {
public function f(){
echo("FinalComponent version<br>")
}
}
// ExtendsFinalComponent.cfc
component extends=FinalComponent {
public function f(){
echo("ExtendsFinalComponent version<br>")
}
}
// finalComponent.cfm
finalComponent = new FinalComponent()
finalComponent.f()
echo("<hr>")
try {
extendsFinalComponent = new ExtendsFinalComponent()
extendsFinalComponent.f()
} catch (any e){
echo("#e.type#<br>#e.message#<br>[#e.detail#]<br>")
}
Here the FinalComponent is declared as final, so it cannot be extended. So the code that just uses it unto itself works fine, but as soon as we try to use a CFC which extends it: error. And here's the output:
FinalComponent version
application
you cannot extend the final component
[C:\scratch\blogExamples\lucee\5\cfcs\final\FinalComponent.cfc]
[]
Not the most polished of exception message there, but at least it works.
OK, so how about a final method? This is a method in a component which itself is not final, but the method is final:
// FinalMethod.cfc
component {
final public function f(){
echo("FinalMethod version<br>")
}
}
// ExtendsFinalMethod.cfc
component extends=FinalMethod {
public function f(){
echo("ExtendsFinalMethod version<br>")
}
}
// finalMethod.cfm
finalMethod = new FinalMethod()
finalMethod.f()
echo("<hr>")
try {
extendsFinalMethod = new ExtendsFinalMethod()
extendsFinalMethod.f()
} catch (any e){
echo("#e.type#<br>#e.message#<br>[#e.detail#]<br>")
}
As one might expect: trying to use code that extends that final method results in an error.
FinalMethod version
application
the function [F] from component [C:\scratch\blogExamples\lucee\5\cfcs\final\ExtendsFinalMethod.cfc] tries to overwrite a final method with the same name from component [C:\scratch\blogExamples\lucee\5\cfcs\final\FinalMethod.cfc]
[]
So far so good. Except the term is "override", chaps, not "overwrite".
It's pretty easy to bypass the finality of a method though:
// BypassFinalMethod.cfc
component {
final public function f(){
echo("BypassFinalMethod version<br>")
}
}
// bypassFinalMethod.cfm
bypassFinalMethod = new BypassFinalMethod()
bypassFinalMethod.f()
echo("<hr>")
function f(){
echo("UDF version<br>")
}
bypassFinalMethod.f = f
bypassFinalMethod.f()
Here, I "override" the final method by simply replacing it with another function using method injection. And this works fine:
BypassFinalMethod version
UDF version
I could not immediately think of a way to bypass
final
at component level though.I think this functionality needs its error messaging cleaned up a bit (it looks a bit amateurish), but it kinda works. Although I don't see the point. And it only kinda works.
So far the only feature of Lucee 5 I've encountered that is any good is
static
methods. But I still think this needs work too.I'll try to revisit abstract functionality shortly too.
Oh, and I still need some responses for my survey, please ("CFML: A quick OO terminology survey" / direct link to survey).
Righto.
--
Adam