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

Reply via email to