groovy-notifications mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From "Dmytro Buryak (JIRA)" <>
Subject [jira] [Commented] (GROOVY-8722) final modifier for non-abstract methods in traits is ignored
Date Wed, 01 Aug 2018 18:30:00 GMT


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:
>             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,
>  * {{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: [|]}}

This message was sent by Atlassian JIRA

View raw message