-
Notifications
You must be signed in to change notification settings - Fork 393
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
WICKET-7145: added @Nonnull
to parameters
#1093
base: master
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm about 1/2 through the review a couple of comments:
-
If there are null check fixes, it would be good if they were in a separate commit so those changes could potentially be back-ported.
-
Please see the code style notes around import static and end-of-file new lines.
@@ -47,6 +45,10 @@ | |||
import org.junit.jupiter.api.BeforeEach; | |||
import org.junit.jupiter.api.Test; | |||
|
|||
import static org.assertj.core.api.Assertions.assertThat; | |||
import static org.junit.jupiter.api.Assertions.assertEquals; | |||
import static org.junit.jupiter.api.Assertions.assertNull; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
static imports usually come before non-static?
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does the Wicket code style call for newline or no newline at end of file?
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Code style EOF new line?
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Code style EOF new line?
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Code style EOF new line?
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Code style EOF new line?
import javax.annotation.Nonnull; | ||
|
||
import static org.junit.jupiter.api.Assertions.assertEquals; | ||
import static org.junit.jupiter.api.Assertions.assertNotNull; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
static imports first?
<dependency> | ||
<groupId>jakarta.annotation</groupId> | ||
<artifactId>jakarta.annotation-api</artifactId> | ||
</dependency> | ||
<dependency> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good time to fix the wicket-native-websocket-core/pom.xml formatting?
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Code style EOF new line
public FilenameWithVersionResourceCachingStrategy(String versionPrefix, | ||
IResourceVersion resourceVersion) | ||
public FilenameWithVersionResourceCachingStrategy(@Nonnull String versionPrefix, | ||
@Nonnull IResourceVersion resourceVersion) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Double check for unintended whitespace
<dependency> | ||
<groupId>jakarta.annotation</groupId> | ||
<artifactId>jakarta.annotation-api</artifactId> | ||
</dependency> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good time to fix pom formatting?
Thanks, I'll fix everything once you're done.
There is only 1 in production code:
I'll check the notes and will make sure all changed files comply with them. |
Is there a reason you went with JSpecify is intended to be the defacto standard and Spring Framework recently migrated all their annotations to it. I recently annotated our main project with it but I went the other way and used JSpecify's recommended strategy of applying |
That would be great, please do. I think there will be users on Wicket 9.x for a while since it is the javax.* based release. |
If JSpecify's approach becomes the standard, it will be in an updated release of the jakarta.annotation API. I do not see any reason to bring in a non-standard dependency when the standardized Jakarta-based solution is sufficient. |
The whole nullability discussion has been going on for years. There are findbugs annotations, checker framework annotations, jetbrains, nullaway, javax/jakarta and many more. All of them have their advantages and disadvantages. JSpecify is an attempt (by Google, Facebook, Uber, etc) to standardize these annotations and define their exact semantics. The jakarta.annotation package is simply not enough. There is no way using only these annotations to define the default nullability of a module/package/class. What does it mean now when a parameter is not annotated? Does it mean it is nullable? Or is its nullability unspecified? JSpecify solves these ambiguities. Having said that, annotating with jakarta.annotation is probably still better than nothing, though I would have preferred annotating with This is not a veto from me, just my opinion. |
I have been a Kotlin developer for years, so I have no knowledge of recent annotation libraries. I chose the Jakarta annotations because the library was already in the BOM. But the annotations for nullability had not been used anywhere yet. As for the other concerns:
|
There is also a huge graveyard of dead projects from large organizations that take passes at solving for short comings. I agree, that the spec could be improved. I agree the the Jakarta Annotation Team should take this up-- if a spec doesn't meet the needs of users, it won't be viable.
If something isn't marked, it should be presumed to be 'nullable', since that is the language default. IMO, less noise is better. Nulls are a feature, not a bug. Null values don't use memory and do not require object allocations that need to be garbage collected. Annotating everything and throwing Optionals everywhere creates non-zero side effects and performance impacts. Related Jakarta Annotation update requested: |
Marking with @jstuyts: Are you consuming Wicket APIs from Kotlin? If so, does adding these |
@theigl Yes, I do. I just tested, and no, they unfortunately don't. Of the many options, I chose the one that is not supported by Kotlin (Jakarta annotations are not listed on this page and not in the code linked to): https://kotlinlang.org/docs/java-interop.html#nullability-annotations I switched to JSpecify, and although that does result in warnings in Kotlin, it won't allow me to specify So it seems to me that JSpecify is not in a usable state yet. Is there a preference for one of the other supported annotations? I don't see a good candidate as most of them are part of a library that is not specifically meant to strengthen contracts, is vendor-specific or is (as good as) obsolete. |
JSpecify is perfectly usable already but it has a couple of non-intuitive annotation usages. Your example is one of them. See https://jspecify.dev/docs/user-guide/#some-subtler-details. You have to either use static imports or write it like this:
Another example is with arrays. I always have to think twice where to put the annotation depending on if the array itself is nullable or its elements are. |
I only found the issues in 9.x. In 8.x the methods are used correctly: https://issues.apache.org/jira/browse/WICKET-7147 |
Got it. I have created a branch using JSpecify: https://github.com/jstuyts/wicket/tree/add-non-null-to-parameters-jspecify The advantages are:
Let me know if you decide you would like to use JSpecify instead of the Jakarta annotations. I will close this PR and create a new one. Another remark about which annotations to use. I think the annotations should be added incrementally, and both annotations (nullable and not nullable) must be added. Once everything is annotated (which can take quite some time), the decision to switch to package-level (or some other level) annotations can be made. It is then also easy to determine which of the 2 annotations results in the least amount of code. The most occurring annotation can be changed to higher-level annotations. |
I prefer the JSpecify branch because of the advantages you mentioned!
What I did in my project is this:
This is basically the incremental strategy that JSpecify recommends. What approach we take will mostly depend on how much time we want to spend. |
Yes, but Wicket is a public project. It is undesirable to have things in a state that is incomplete, I think, as this would result in a lot of errors/warnings for users of Wicket. Doing everything at once would result in a giant PR. I think this one is already stretching the limit. I have a few more coming if this (or the JSpecify variant) is merged, and will make sure those PRs are smaller. So I think that higher-level declarations of (non-)nullability is not an option for now. |
Agreed. That only makes sense if we completely annotate a whole module or at least a whole package. I think we can go ahead with your current JSpecify PR and continue improving the annotations in future work. The only minor concern I still have with this PR is that we are adding And then there is the question of how we continue with this and what we require in future PRs. Is our goal to completely annotate the whole Wicket codebase? Do we add nullability annotations whenever we touch existing code? Do we require them in PRs? Or is this a one-time effort to slightly improve the developer experience? |
I'll wait for what the final decision will be: Jakarta or JSpecify.
Like I said before: once everything is annotated, it is easy to decide which of the 2 annotations can stay.
A recommendation to add the annotations when adding or modifying code is the least that can be done. Maybe split PRs: first a PR that adds the annotations to existing code, and then a PR that contains clearer code for the modifications because of the added annotations. This should make reviews easier. Annotating everything would be best, but there are some tricky situations, that force clients that can never see a
|
For me both are meh.
But if others decide to use it in Wicket then I'd vote for JSpecify. |
Yeah, both of these concerns are valid. In my experience, when a whole package is marked as I'm also unsure about proceeding with this if we don't have a concrete and useful final result in mind. |
The desired end result would be that for every variable and method in Wicket it is known whether it is nullable or not. This may take a very long time to achieve. And may even be unsatisfactory: for some situations a client is guaranteed that the value will never be So the perfect state where you know exactly what is nullable will likely never be achieved. Even so, even adding nullable information to the most-used types of Wicket will greatly improve the developer experience. Yes, it is too bad that nobody took the time to add the annotations to less-used parts. And yes, it is unfortunate that a value is marked as nullable that will never be Both issues can be solved: more effort by people who want to contribute, and new, more strict APIs and backward-incompatible changes to contracts of existing APIs (if acceptable for Wicket users). In my opinion adding nullability information to even a small number of places, makes Wicket easier to use. This will hopefully help convince developers that Wicket still is a good choice for front end development. Note that knowing what is nullable is one of the reason I love working with Kotlin: no more going through incomplete documentation, and then through multiple layers of code to figure out whether or not I should handle |
For seamless Kotlin integration, having proper @nullable and @NotNull annotations is extremely helpful. We're using Kotlin with Wicket, and one challenge we've encountered is the need to manually remove nullability when overriding methods. This adds unnecessary friction and makes the combination of Kotlin and Wicket feel less fluent. Improving support for nullability annotations would greatly enhance the developer experience. |
I thought about this some more over the weekend: It took me about 30 hours to fully annotate our ~350KLOC codebase by using the approach described above. Wicket is much smaller than that and we probably only need to annotate the most important packages in I would suggest the following approach:
That way individual PRs should be relatively small and easy to review and by using only Ideally, these changes would be driven by someone who actually uses Wicket with Kotlin and can verify the improvements. |
@theigl I tried your approach, but I think it is more hassle than adding I added I made a copy of default inspection profile of IntelliJ with any inspection not related to
So using Using
So let's go back to my original goal: make it more pleasant to work with Wicket (primarily for Wicket users). I prefer a pragmatic approach here:
If it is decided that If the position changes to: any Possibly, we will maintain the |
Added
@Nonnull
to parameters that are checked fornull
. Also included some method overloads/variants.In addition found some bugs in
null
checks.See https://issues.apache.org/jira/browse/WICKET-7145 for details.