trailing comment
Alex Buckley
alex.buckley at oracle.com
Thu Jan 10 13:01:10 PST 2013
On 1/10/2013 10:10 AM, michael keith wrote:
> On 09/01/2013 9:12 PM, michael keith wrote:
>> On 08/01/2013 8:27 PM, Alex Buckley wrote:
>>> Hi Mike,
>>>
>>> On 1/7/2013 7:03 PM, michael keith wrote:
>>>> - p. 4 - Is there a use case for allowing @FooContainer to be
>>>> @Documented/@Inherited when @Foo is not? In most cases seemingly
>>>> arbitrary limitations are bad, but in this case I don't see a need for
>>>> allowing it, and it might actually be detrimental to do so. For
>>>> example,
>>>> if I make a mistake as the annotation developer and during the
>>>> course of
>>>> development I no longer want @Foo to be @Inherited so I remove the
>>>> meta-annotation from @Foo but forget to remove it from @FooContainer.
>>>> When a user goes to use @Foo and annotates their UserClass with it
>>>> their
>>>> UserSubClass won't see it, as expected. However, if they add another
>>>> @Foo then both will be wrapped in a @FooContainer and now UserSubClass
>>>> will be able to see them using getAllAnnotations(Foo.class) because the
>>>> container is inherited. I am just wondering if the feature was
>>>> motivated
>>>> by a case that someone thought would be useful, or if it was a case of
>>>> trying not to impose unnecessary limitations. In general I am concerned
>>>> about the maintenance issue of keeping the two annotations in sync. If
>>>> they are allowed to be out of sync then the IDE won't be able to
>>>> help me.
>>>
>>> Simplicity a.k.a. "both are @Inherited, or neither is" is good, but
>>> unnatural because there's no solid link from a containing annotation
>>> type to a repeatable annotation type. Consider these types:
>>>
>>> @Inherited
>>> @Repeatable(FooContainer.class)
>>> @interface Foo {}
>>>
>>> @Inherited
>>> @interface FooContainer { Foo[] value; }
>>>
>>> @Inherited
>>> @interface RandomContainer { Foo[] value; }
>>>
>>> When compiling Foo, you can't fail to notice @Repeatable, so it's
>>> reasonable to load up the named FooContainer and enforce
>>> "Foo-is- at Inherited --> FooContainer-is- at Inherited".
>>>
>>> When compiling FooContainer and RandomContainer, you don't check
>>> anything about Foo. The presence of Foo in a return type is not
>>> significant enough for a compiler to load Foo and ponder a two-way
>>> relationship. It would be a wasted effort for RandomContainer, a
>>> perfectly legal type which has nothing to do with repeatability. For
>>> FooContainer, the compiler would be happy that it and Foo are
>>> @Inherited, but that's not authoritative as I can swap in a
>>> non- at Inherited FooContainer.class at runtime. (That's why core
>>> reflection has the AnnotationFormatError check on page 10.)
>>>
>>> In summary, I think the compiler should not be expected to "save" an
>>> annotation type owner who removes @Inherited from Foo but leaves it
>>> on FooContainer.
>>
>> I'm not convinced the compiler needs to verify both directions; I
>> think it only needs to verify Foo. Foo is the annotation of interest,
>> the one that will get modified during development. FooContainer is the
>> uninteresting "support annotation" needed for repeatability and will
>> likely be completely ignored once created. The only way for it to be
>> "out of sync" with Foo is if somebody goes back and modifies
>> FooContainer after having compiled Foo. In that unlikely case it is no
>> different than if somebody removes a method from a class and
>> recompiles the class, even though another class may be invoking the
>> removed method. Either the IDE "saves" or figures out the referencer
>> and an error shows up on the referencing class, or an exception occurs
>> at runtime. In our annotation case, the IDE could do the same, i.e.
>> remember the annotation/container pairs, or the AnnotationFormatError
>> will be eagerly thrown when Foo is first loaded. That makes sense to
>> me, and seems a more reasonable behavior than the incorrect and
>> unexpected outcome in the scenario I described earlier.
>
> I should also have mentioned that the current restrictions aready result
> in the load-time exception (but no compile-time exception) if a
> container type is modified to *not* be @Inherited, when Foo is. Because
> there is no link back to Foo, both that case plus the case of a
> containing annotation type having a lesser Retention setting than the
> type it contains, will not be caught when compiling the containing type.
> They will only be noticed when the contained annotation type is loaded
> and validated for well-formed repeatability.
I agree with everything you say above. What I should have said more
clearly is this: there is no "use case" per se for allowing FooContainer
to be @Documented/@Inherited when Foo is not. It's just that we can't
easily detect it at compile-time, due to separate compilation. It slips
by as something you can do, but probably shouldn't. There are an
infinite number of programs in that vein "allowed" by the JLS. Since the
scenario is ultimately harmless, we don't need to prevent it at runtime
either.
In the dual case - Foo is @Documented/@Inherited but FooContainer isn't
- there is clearly badness. Since we can't detect it at compile-time,
there is a runtime check.
Alex
More information about the enhanced-metadata-spec-discuss
mailing list