[
https://issues.apache.org/jira/browse/GROOVY-8722?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=16565779#comment-16565779
]
Dmytro Buryak commented on GROOVY-8722:
---------------------------------------
Paul, thanks for the great explanation!
Well, at least now it's clear that this behavior is expected. Before your
answer there were only guesses)
I feel that the best option would be to mention about
{{{color:#707070}final{color}}} keyword in Traits:Limitations documentation
section. Decision about how the compiler should handle such situation is also
not an easy one. Basically because it's possible that final keyword is already
scattered along many projects that were successfully compiled and published,
and many others "what if ...".
Let me share some of my thoughts:
In your comment I see a perspective of a language designer. But let met give
some reasoning from a language user perspective, just a little bit different
angle.
In Brian's stackoverflow comment, that you've mentioned, I see one problem
with the hypothetical A,B,C example if final default methods were supported in
Java. He says that "Now C is irretrievably broken". I don't see any reason why
this is bad. Instead, I feel that it would be great if compiler detected such
situation. Designer of B by using final keyword literally said "if you are
instanceof B then your void foo() method +must+ do exactly this, otherwise you
break B's expectations of behavior that I put in it, don't override it". So
designer of C did exactly that what he was warned not to do: he inherited B but
overridden foo().
But this is all hypothetical: Java interfaces are all about "what +you can do+
with object" and not "what +object can do+ and how it behaves", basically
because they don't have state and don't encapsulate piece of functionality.
Default methods of Java 8 made interfaces one step closer to traits, but I feel
that was more dictated by Stream API appearance and the need to change existing
collections API to make it happen (just as Brian Goetz mentioned). So I assume
that the need for final methods in interfaces for Java developers is pretty low.
Groovy traits, on the other hand, serve as a "composable component of
functionality" (thanks to Cedric Champeau for definition) and they were such
components from the very beginning. I mean this is more a value for the
developer (language user) that traits provide, rather than definition. And from
this perspective, having {{{color:#707070}final{color}}} keyword supported in
traits would be great. If developer was designing for example Mammal trait with
several methods (speak(), move()) that +may+ be redefined by some exotic
implementations, but wanted to disallow to change some functionality (like
feedWithMilk(), buildPlacenta()), then final keyword would do the job. And from
the developer perspective, if he marks something as "final" in any component of
functionality, not only trait, that means intention "this can't be redefined or
it would break otherwise". At the moment, if I need to do this, I have no other
option than just to put a comment like "do not override" in trait method doc
and hope that trait user will read it. By the way, my example project that is
linked in the issue description, shows similar real life situation: simple but
useful trait "Suspendable", where some methods shouldn't be overridden or it
almost certainly would fail to do it's job correctly.
All this said and going further, I totally agree with your definition of final
methods in traits as it fully supports what I've described above. And it sounds
very natural to me. And moreover very usable.
Regarding api evolution. I don't see much impact here, or I just misunderstood
your point.
I see only one big problem because of compiler silently discards final
modifier.
Suppose groovy "x.y" doesn't support final in traits, and groovy "x.z" adds
support for it using definition you've proposed. All compiled with "x.y" and
prior will work fine with "x.z", as it all is final-less. If you use binary.
But when you take sources that worked correctly with "x.y", there's no any
guarantee that there's no "final" keyword somewhere, and when you compile it
with "x.z", then compilation could fail. Resulting in no backward
compatibility. And another reverse situation: if you get source written for
"x.z" and compile it with "x.y", it won't fail but discard all final keywords
and it's behavior becomes different from the original design.
> final modifier for non-abstract methods in traits is ignored
> ------------------------------------------------------------
>
> Key: GROOVY-8722
> URL: https://issues.apache.org/jira/browse/GROOVY-8722
> Project: Groovy
> Issue Type: Bug
> Components: Compiler, Documentation
> Affects Versions: 2.4.15, 2.5.1
> Reporter: Dmytro Buryak
> Priority: Major
> Labels: documentation
>
> {{When use {color:#707070}final{color} modifier in trait non-abstract method
> signature, then:}}
> * {{compiler successfully compiles trait and class that implements it}}
> * {{final method of trait may be overridden in class that implements trait}}
> * {{if class implements trait and doesn't override trait final method, then
> this method is available as non-final method in class}}
> * {{documentation says absolutely nothing about this}}
> {{In other words, {color:#707070}final{color} modifier is ignored in trait
> methods: code works the same with or without it. Even if this behavior is
> expected, there's nothing about it in the documentation.}}
> {{Here's simple example gradle groovy project to demonstrate the issue:
> [https://github.com/dburyak/groovy-bug-trait-final|http://example.com]}}
--
This message was sent by Atlassian JIRA
(v7.6.3#76005)