I have finished learning annotations in my textbook, but one thing does not make any sense to me. I know how to create custom annotations, how to specify targets, and how to extract information out of them from classes at runtime using reflection. But how do these annotations actually perform the action of metadata to compilers and runtime? The annotations as a type of interface-like cannot have function bodies, so this is very confusing
#How do annotations provide metadata to compilers and runtime, exactly?
1 messages · Page 1 of 1 (latest)
<@&987246399047479336> please have a look, thanks.
Here is an AI assisted attempt to answer your question 🤖. Maybe it helps! In any case, a human is on the way 👍. To continue talking to the AI, you can use </chatgpt:1108714622413963314>.
I have finished learning annotations in my textbook, but one thing does not make any sense to me. I know how to create custom annotations, how to specify targets, and how to extract information out of them from classes at runtime using reflection. But how
Annotations in Java provide metadata to compilers and runtime by allowing developers to attach additional information to classes, methods, fields, and other program elements. This metadata can be used by the compiler to generate code, perform validation checks, or modify behavior at runtime.
When you create a custom annotation in Java, you define its structure using the @interface keyword. This allows you to specify elements that can hold values such as strings or numbers. These elements can then be accessed using reflection at runtime.
Annotations are processed by the compiler and can be used to trigger specific actions or behaviors. For example, the @Override annotation tells the compiler that a method is intended to override a superclass method, while the @Deprecated annotation marks a method as obsolete and informs users that it should no longer be used.
At runtime, annotations can be retrieved using reflection and used to make decisions about how a program should behave. For example, an annotation could be used to specify transactional boundaries in a database operation or configure aspects of dependency injection.
Overall, annotations provide a powerful mechanism for adding metadata to Java code that can be used by both the compiler and runtime environment for various purposes.
- AI generated response
wow, this AI is useless
But how do these annotations actually perform the action of metadata to compilers and runtime?
what do you mean by "perform the action of metadata to compilers and runtime"?
It's just ChatGPT afaik.
like how for example, the @Override gives compiler warning when you try to override a method that doesnt exist in base class or interface
How does it achieve this functionality
and how can I create an annotation like this, say for example, I wanna make an annotation @Digit which can only applied to integer data types, but will give compiler error if the number is not within 0 to 9
I don't believe there is an answer for this, because I'm pretty sure every compiler can decide that for themselves
The annotation itself doesn't do anything, it's the compiler that checks for its presence
If you make a custom annotation it's up to you to write code elsewhere that checks for it
You mean, I would need to write a custom compiler? But that doesnt make any sense..
Spring uses many annotations, especially the restcontroller but they seem to all work without customized compilers or JVMs
Spring is open source so you don't need to figure how it work, look inside:
`RequestMapping` annotation is handled by `RequestMappingHandlerMapping`, see `getMappingForMethod` method. `Autowired` annotation is handled by `AutowiredAnnotationBeanPostProcessor`, see `processInjection` method.Both use reflection to get annotation data and build the handler mapping info in the first case or populate the bean in the second one.
Huh, I know how reflection can extract annotation data but how does that enable compiler warnings? So confusing
You (nor Spring) can create compiler warnings with your annotations
(except by annotation processors)
This article looks actually pretty good for that question: https://medium.com/@denis.verkhovsky/spring-how-annotations-work-part-1-3f08f34ff36f
*I've only skimmed it though
I did take a read, but this example really puzzles me. It seems as though this annotations was able to force the compiler to add a new getter method to the class
Hmm, I know reflection can read class data, can it actually modify the class at compile time too?
That's an example using an annotation from Lombok.
To be honest, I can't explain that example either, but fortunately it's not really relevant for the rest of the article anyway
Fair enough, but seeing these annotations do these mysterious things is very unsettling
Gets in the way of a true understanding of language
If I have to pretend like this annotations are some black box
Yeah, I know.
Python is also sorta magic in that regards, but it's still much easier to look up what is happening

@hollow bolt I just wrote about this
Book teaching how to write modern and effective Java.
no
lombok breaks java rules when it does so
wtf
Python example, in case you don't know what it looks like there:
import sys
def memoize(func):
memo: dict = {}
def wrapper(*args, **kwargs):
key = str(*args) + str(**kwargs)
if key not in memo:
memo[key] = func(*args, **kwargs)
return memo[key]
return wrapper
@memoize
def fibonacci(n: int) -> int:
if n < 2:
return n
return fibonacci(n - 1) + fibonacci(n - 2)
if __name__ == '__main__':
sys.setrecursionlimit(1000000)
print(fibonacci(10))
print(fibonacci(100))
print(fibonacci(1000))
Yeah lombok is more a "C++" than a Java library
normal sanctioned uses of annotations cannot do that
they are just structured comments
they can be read at compile time - and you can do stuff like that
and you can read them at runtime
and then use reflection to do stuff
Apologies, I dont have a good python background, I can still try to understand your example tho
But basically, its able to assist the Fibonacci recursive implementation by using itself to cache the computations?
but they are just structured comments
python decorators are different
Yeah figured, that makes sense, thats how it was presented to me as well
python decorators aren't just comments
Which is why it made no sense how Lombok could modify the class like that
in python this:
@memoize
def fibonacci(n: int) -> int:
if n < 2:
return n
return fibonacci(n - 1) + fibonacci(n - 2)
is the same as this
def fibonacci(n: int) -> int:
if n < 2:
return n
return fibonacci(n - 1) + fibonacci(n - 2)
fibonacci = memoize(fibonacci)
its a shorthand for a "wrapped reassignment"
so any "function that takes a function and returns a function" can be used as a python decorator
Ooooh, so this memoize is a built-in function in python that abstracts away all the creation of dicts to store value
Is it? No it isn't, no?
its not builtin in this example
def memoize(func):
memo: dict = {}
def wrapper(*args, **kwargs):
key = str(*args) + str(**kwargs)
if key not in memo:
memo[key] = func(*args, **kwargs)
return memo[key]
return wrapper
Ooooh
since here its just a function @naive nexus wrote
It would be:
def fibonacci(n: int) -> int:
if n < 2:
return n
return memoize(fibonacci(n - 1)) + memoize(fibonacci(n - 2))
fibonacci = memoize(fibonacci)
no?
Oh okay I understand now
But from what I can tell, python relies on functions being first class citizen to do this
Coz the function is being passed as an argument to this memoize function
exactly
And a new function is returned which is basically the original function with memoization assistance
nah it doesn't go into the function body
at least I don't think
Yes it does, otherwise how would it do the caching?
but the global rebind affects it
but anyways - thats not how java does @Thing
in java its just a comment thats retrievable by code
annotation processors can read them at compile time to generate new files or make static checks
other tools can read them in the classfile to do whatever with them
But tbh, how does lombok break the rules? It is an annotations allowed on the java ecosystem, but was it somehow "externally modified"
and you can read them at runtime with reflection
lets first define what "valid java" is
Huh annotation processors. Are these like custom compilers or simply utility classes that do reflection and extract details?
compiler plugins
Ah
a full program is "valid java" if it doesn't violate the rules of the java language
one of those rules is "a class defines all its members"
so
class A {
}
class B {
void main() {
new A().f();
}
}
is definitely invalid java
because we already saw a definition of the A class
and it does not include a definition for f
class A extends A2 {
}
class B {
void main() {
new A().f();
}
}
is maybe correct, maybe invalid
we don't know for sure until we have a definition for A2
True. But basically lombok hijacked the class and added a method not explicitly defined
and that turns what should be invalid code into valid code
hence being a different language
the ways it hijacks the compiler is not through actually supported apis
So in essence, the @Getter was able to:
-
add a new method not explicitly define
-
allow other classes to call this method as though it was defined by that class itself?
yes
Wtf
Wow...
since its really "the lombok language"
coincidentally implemented by hooking into the java compiler
People who use lombok a lot are aware of this crazy risk
An awful lot of trust into something which is essentially the equivalent of arcane magic
Coz it does something impossible in Java
its perfectly fine to take the risk - like kotlin is also a different language
but the fact that the risk/reality is obscured is annoying for everyone
lombokc ... would be a lot nicer
lombokc? Is that different from lombok?
im saying that it would be nicer if there was a lombokc compiler for lombok
Ohh like javac and kotlinc
@hollow bolt You can ignore this message.
*Just because I wanted to absolutely make sure (this will be the last Python message I'll send here 🐒 ):
def print_result(func):
def wrapper(*args, **kwargs):
print(res := func(*args, **kwargs))
return res
return wrapper
@print_result
def f(n) -> int:
if n < 0:
return 0
return n + f(n - 1)
def g(n):
if n < 0:
print(0)
return 0
print(res := n + g(n - 1))
return res
if __name__ == '__main__':
print("-----")
f(4)
print("-----")
g(4)
```prints:
0
0
1
3
6
0
0
1
3
6
So it does indeed need to go into the function body, but it's not necessarily possible to just use the annotation function how I tried to do, but you have to actually re-write the annotation implementation (in this case the `print`s).
If we want to use the `print_result` function in the body, then it starts to look like a real mess...
```py
def g(n):
if n < 0:
return print_result(lambda: 0)()
return print_result(lambda: n + g(n - 1))()
Ah I see. Since java method of defining annotation does not allow us to specify a body implementation, we cannot achieve this kind of behavior in Java, atleast not without seeking the dark side (beyond Java) like Lombok did
so there is one workaround
you can in a file like this
@Getter
class B extends A {
String s;
}
generate a new file that looks like this
class A {
String getString() {
return ((B) this).s;
}
}
and that is kosher because you changed nothing about how B is written
This makes sense for the first part. But how do you trick the other classes into thinking A as B
thats just a cast
A needs to masquerade as B now
yeah
I made a library which does this https://github.com/bowbahdoe/magic-bean?tab=readme-ov-file#i-receive
Wait I completely missed the part where you put extends A
yeah, that one's kinda important
But wait, that was not specified in the article. So even then, the annotation is able to automatically extend from a base class
Weird weird
no
you need to write the extends yourself
so you are extending a class that doesn't exist
you compile and an annotation processor generates a new file
Ooooh
but now we have this annotation processor to worry about. Where does it come from
And how do we know everyone who uses this annotation will get one
you have to seperately put the processor on the --processor-path or --processor-module-path when you run javac
here is how you make one
Ahh, I assume IntelliJ sorta does that under the hood
And when you import lombok in maven, the dependency itself has the annotation processor built in, with instructions?
yes
That is very interesting
and it goes beyond the allowed api of an annotation processor
but its also the most popular "annotation processor"
and thus defines peoples expectations for what an annotation can do
which, unideal
wow it breaks the rules even here
Its popular coz it gives them power otherwise unobtainable
part of the reason why I switched to kotlin for personal projects is because it has all the functionality provided by lombok in the language itself
at long last my classes don't look like christmas trees
wow another dub for Kotlin