-
-
Notifications
You must be signed in to change notification settings - Fork 2.4k
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
[FEATURE] Collection field marked as @Singular should be able to accept null #2221
Comments
To add some more context: While I agree that in general, Also, since we serialize our objects via JSON, setting these collections to As it stands right now, the only acceptable course of action is to stop using |
Also left a comment in #819 explaining why |
I think it would be a good idea to create a parameter for the annotation to allow nulls optionally, as in some places one wouldn't want to have nulls going around inside thee collections. |
@cezar-tech - Just to be clear, this is about making it so calling the PLURAL version of the builder method with |
Voting for this issue, as it causes NPE when using JacksonDeserialize with builder pattern. |
A parameter to work around a scenario where code treats a null collection different from an empty one? No, we don't cater to obviously outdated, badly in need of an update weirdo code. In general, the 'eh just add a parameter' mindset is unlikely to be accepted; we are quite sensitive to growing the param list to hundreds of entries. I can see a change where .plural(null) is allowed. It still is real weird though, and I think that causes more harm than good, because it's so unclear what that means: the .plural(collection) method ADDS each element in the provided collection to the builder, it does not REPLACE, hence, accepting nulls doesn't really make sense. Can't you just work around this stuff by manually writing the getter for the collection field, and explicitly returning null if it is empty? |
@rzwitserloot - In retrospect, the specific scenario I found myself in that prevented the use of Singular is indeed a silly one-off, and not one I expect you to address specifically. However, I still believe that allowing developers to use Singular while still being able to set the collection itself to a specific instance (or to null) has value, and that it can be done cheaply and in a very user-friendly way. First off, if nothing else, could we get something other than a generic NullPointerException in this case? Perhaps a message like "Cannot pass a null array/collection into a Singular builder"? This would make Lombok much easier and friendlier to use, and it would improve debugging significantly. Second, it still would be nice to support a case where you CAN set a collection explicitly while still getting the benefits of the Singular builder. The two suggestions I have for this are: Either make it possible to set the default state of the field until a value is added to it (e.g. make it null or THE empty collection, etc.), or add another method like "replace/setMyCollection" that can replace the field reference. Singular already adds "clearMyCollection()" to clear the contents of a collection, so it should be reasonably easy to also include one where the collection itself is replaced. And this wouldn't need to break the contract of the existing methods. Being able to explicitly set (or default) an instance to have an empty, immutable collection like Thank you for your time. |
The case being, specifically, passing As for a replace option.. I can see that, too. The issue with replace is more palatable; I doubt It is, however, quite complex to let you replace the entire thing. The field that backs a singular-collection need not be of the type at all; for example, maps have 2 lists as backing collection! Thus, the creation of this method would require 4 fields in the builder for a single field: a list for the keys, a list for the values, a boolean to know that an explicit map was provided, and a field of type map, for this default (and we can't use There is absolutely no need whatsoever to being able to explicit set an instant to have an empty, immutable collection – you already get this with Lombok is extremely intelligent and efficient about this stuff: If you don't call the singular method at all, we don't even make those lists, we leave em as null, and then call Effectively, then, there is really only one open use-case, which is leaving the field as |
@FatCash I guess the way to solve that deserializer issue is if |
I wrote down a full proposal: https://github.com/rzwitserloot/lombok/wiki/FEATURE-PROPOSAL:-null-checking-@Singular - please provide feedback. |
I think that proposal looks great. Thank you for considering it. :) And thanks for the explanation on why that
Yep, that's what I was referring to. If my collection is named |
Alternatively, can we consider simply null-checking the input and treating |
@KieferSkunk is it? See, that's what I was wondering. In my proposal, it is configurable, but defaults to nullpointerexception (WITH a nice message). The general tendency to go 'null? Oh.. just... silently do nothing' is not generally a good idea. Perhaps 9 out of 10 times that is the intended behaviour, but that 10th time is a doozy: Silently doing nothing is, literally, on the order of 100x worse than throwing an exception, because they are that much harder to find. A large part of the 'cost' of writing a software bug is finding out about it, so making that very hard (which 'silently ignore null' does!) is an extremely bad plan. Hence my hesitance in just defaulting to that behaviour. |
This is one of those areas where newer languages like Go and Kotlin are actively trying to deal with this sort of problem, by making it much harder to get into situations where NullPointerException-type errors are even possible. While Java might favor fully-explicit "Nulls are always bad", I feel like we have an opportunity to start moving the needle a bit on that. Especially given that so many third-party libraries (and even some built-in Java functions) exist to work around the fact that collections can be null - e.g. SpringFramework's CollectionUtils.isEmpty() method, which checks for both null and empty. I don't know about other developers, but in situations where I want to add or copy values from one collection to another, do a transformation, etc., (And I appreciate that you're proposing to make this configurable.) |
Kotlin and Go add nothing relevant here; if java could snap its fingers and toss into the garbage 25 years of built-up community effort, null handling would be better in a heartbeat. But if we start holding 'it has a successful community and a history' against languages, we're signing up as developers to go through a rigamarole where every 15 years or so we gather, build a giant bonfire, ritually burn the world down, and start over. Count me out! That kind of cheerleading of new languages (it's better cuz no cruft) annoys the heck out of me and sounds like a really, really bad thing to do as citizen of the developer community. Praise kotlin for smart new tricks that are nice; do not praise it for freebies it got by pressing a reset button. Secondly, what you're describing is a bad way to do it. null is not and never has meant 'empty'. If in your code it means that, that is bad. Stop doing it. null, if it means anything, means 'no value', and 'no value' is not the same as 'empty value'. If EVER you find yourself writing If you're not sure, don't 'defensively code' and add the null check, not knowing if it is ever going to come up. Don't do that; let the NPEs guide you, for they are good: They point at where you made a mistake. Now you can fix it. The alternative is that your code is hard to read, hard to test (a null check implies that the value could be null; this would be confusing if it cannot be, and would erroneously lead developers working on this code to be confused about whether some value can contain null or not. That is precisely the kind of thing that got us in this mess in the first place!) – and you're building up tech debt. These are bad things; so don't go there. Write clean code that doesn't null check, and fix methods that are too cavalier in returning null as you hit the NPEs during development. It saves time and makes you more productive in the end, and leads to far cleaner and consistent codebases. The proposal's default behaviour (throw NPE, with clear messaging) stands, and if it is going to have the ignore option at all, I'm going to write it with pain in my soul. I hope I'm catering to bridge code (jackson deserialization, for example), and not enabling bad code practices. I know a lot of java coders do the 'null means empty' thing. But many more don't, and I don't want lombok to require a ton of stylistic configuration before it really meshes with your style. I would rather that it then only caters to a single style and has friction with others, especially if the styles it doesn't support well are inferior to the one it works well with. |
That's fine, we can agree to disagree on this point. I've always taken the approach of "Whatever makes sense" when solving problems. Your proposal is fine as it is. I didn't really want to come here and start another language war. I just felt that these suggestions might be helpful. I'll leave you alone now. |
I will just respond to one thing you said here:
This is a very good ideal. Unfortunately, the reality in every business I've been a developer for is that we don't have control over what our external libraries do, we don't necessarily know what other developers in the same company (different groups with their own practices) do, and things slip through the cracks. When NPEs occur in production because they weren't caught in test, they not only cause developer pain, they affect real customers. As such, it's been my practice to always defensively code, to try to catch as many of these potential errors as possible before they happen. It's exhausting to have to be so hyper-vigilant all the time, but it's the reality we find ourselves in frequently in the business world. |
I've written almost all of it, except the bit that tags the param with an appropriate nullity annotation. My original plan was to look at the source file and take a wild stab as to which one(s) are appropriate. But now that I think about it some more this sounds like a really bad plan. It'd mean that whether or not your plural method gets an annotation depends on whether or not you use it elsewhere. That sounds like a fine way to produce inconsistent code, where some plural methods have em and some don't. I can't 99.5%+ rely on SOMETHING being in the source file I'm in to give away which annotation(s) I can generate. Even if I disregard the various I guess I have to resort to a config key, which is a bit ugly, but at least then there's a way out, and we can apply it to other lombok stuff I guess. |
…argument with configurable results.
…llchecks its argument with configurable results.
…nullity annotations. Which 'flavour' is defined in lombok.config; applied to toString, equals, canEqual, and plural-form of `@Singular`.
…enerated plural form nullchecks.
While building orca with upgraded spring boot version 2.3.12, google error-prone package throwing IndexOutOfBoundException as given below: orca/orca-api/src/main/java/com/netflix/spinnaker/orca/api/pipeline/TaskResult.java:32: error: An unhandled exception was thrown by the Error Prone static analysis plugin. @builder ^ Please report this at https://github.com/google/error-prone/issues/new and include the following: error-prone version: 2.4.0 BugPattern: FallThrough Stack Trace: java.lang.IndexOutOfBoundsException at java.base/java.nio.HeapCharBuffer.subSequence(HeapCharBuffer.java:633) at java.base/java.nio.HeapCharBuffer.subSequence(HeapCharBuffer.java:41) at com.google.errorprone.bugpatterns.FallThrough.matchSwitch(FallThrough.java:70) at com.google.errorprone.scanner.ErrorProneScanner.processMatchers(ErrorProneScanner.java:451) at com.google.errorprone.scanner.ErrorProneScanner.visitSwitch(ErrorProneScanner.java:825) at com.google.errorprone.scanner.ErrorProneScanner.visitSwitch(ErrorProneScanner.java:152) at jdk.compiler/com.sun.tools.javac.tree.JCTree$JCSwitch.accept(JCTree.java:1229) at jdk.compiler/com.sun.source.util.TreePathScanner.scan(TreePathScanner.java:82) The error is due to Lombok lib upgrade from 1.18.16 to 1.18.20 as transitive dependency of Spring boot. Similar to the issue mentioned below google/error-prone#2575 Fix: To remove @Singular annotation from classes using Lombok builder pattern. Reference: projectlombok/lombok#2221
While building orca with upgraded spring boot version 2.3.12, google error-prone package throwing IndexOutOfBoundException as given below: orca/orca-api/src/main/java/com/netflix/spinnaker/orca/api/pipeline/TaskResult.java:32: error: An unhandled exception was thrown by the Error Prone static analysis plugin. @builder ^ Please report this at https://github.com/google/error-prone/issues/new and include the following: error-prone version: 2.4.0 BugPattern: FallThrough Stack Trace: java.lang.IndexOutOfBoundsException at java.base/java.nio.HeapCharBuffer.subSequence(HeapCharBuffer.java:633) at java.base/java.nio.HeapCharBuffer.subSequence(HeapCharBuffer.java:41) at com.google.errorprone.bugpatterns.FallThrough.matchSwitch(FallThrough.java:70) at com.google.errorprone.scanner.ErrorProneScanner.processMatchers(ErrorProneScanner.java:451) at com.google.errorprone.scanner.ErrorProneScanner.visitSwitch(ErrorProneScanner.java:825) at com.google.errorprone.scanner.ErrorProneScanner.visitSwitch(ErrorProneScanner.java:152) at jdk.compiler/com.sun.tools.javac.tree.JCTree$JCSwitch.accept(JCTree.java:1229) at jdk.compiler/com.sun.source.util.TreePathScanner.scan(TreePathScanner.java:82) The error is due to Lombok lib upgrade from 1.18.16 to 1.18.20 as transitive dependency of Spring boot. Similar to the issue mentioned below google/error-prone#2575 Fix: To remove @Singular annotation from classes using Lombok builder pattern. Reference: projectlombok/lombok#2221
While building orca with upgraded spring boot version 2.3.12, google error-prone package throwing IndexOutOfBoundException as given below: orca/orca-api/src/main/java/com/netflix/spinnaker/orca/api/pipeline/TaskResult.java:32: error: An unhandled exception was thrown by the Error Prone static analysis plugin. @builder ^ Please report this at https://github.com/google/error-prone/issues/new and include the following: error-prone version: 2.4.0 BugPattern: FallThrough Stack Trace: java.lang.IndexOutOfBoundsException at java.base/java.nio.HeapCharBuffer.subSequence(HeapCharBuffer.java:633) at java.base/java.nio.HeapCharBuffer.subSequence(HeapCharBuffer.java:41) at com.google.errorprone.bugpatterns.FallThrough.matchSwitch(FallThrough.java:70) at com.google.errorprone.scanner.ErrorProneScanner.processMatchers(ErrorProneScanner.java:451) at com.google.errorprone.scanner.ErrorProneScanner.visitSwitch(ErrorProneScanner.java:825) at com.google.errorprone.scanner.ErrorProneScanner.visitSwitch(ErrorProneScanner.java:152) at jdk.compiler/com.sun.tools.javac.tree.JCTree$JCSwitch.accept(JCTree.java:1229) at jdk.compiler/com.sun.source.util.TreePathScanner.scan(TreePathScanner.java:82) The error is due to Lombok lib upgrade from 1.18.16 to 1.18.20 as transitive dependency of Spring boot. Similar to the issue mentioned below google/error-prone#2575 Fix: To remove @Singular annotation from classes using Lombok builder pattern. Reference: projectlombok/lombok#2221
While building orca with upgraded spring boot version 2.3.12, google error-prone package throwing IndexOutOfBoundException as given below: orca/orca-api/src/main/java/com/netflix/spinnaker/orca/api/pipeline/TaskResult.java:32: error: An unhandled exception was thrown by the Error Prone static analysis plugin. @builder ^ Please report this at https://github.com/google/error-prone/issues/new and include the following: error-prone version: 2.4.0 BugPattern: FallThrough Stack Trace: java.lang.IndexOutOfBoundsException at java.base/java.nio.HeapCharBuffer.subSequence(HeapCharBuffer.java:633) at java.base/java.nio.HeapCharBuffer.subSequence(HeapCharBuffer.java:41) at com.google.errorprone.bugpatterns.FallThrough.matchSwitch(FallThrough.java:70) at com.google.errorprone.scanner.ErrorProneScanner.processMatchers(ErrorProneScanner.java:451) at com.google.errorprone.scanner.ErrorProneScanner.visitSwitch(ErrorProneScanner.java:825) at com.google.errorprone.scanner.ErrorProneScanner.visitSwitch(ErrorProneScanner.java:152) at jdk.compiler/com.sun.tools.javac.tree.JCTree$JCSwitch.accept(JCTree.java:1229) at jdk.compiler/com.sun.source.util.TreePathScanner.scan(TreePathScanner.java:82) The error is due to Lombok lib upgrade from 1.18.16 to 1.18.20 as transitive dependency of Spring boot. Similar to the issue mentioned below google/error-prone#2575 Fix: To remove @Singular annotation from classes using Lombok builder pattern. Reference: projectlombok/lombok#2221
While building orca with upgraded spring boot version 2.3.12, google error-prone package throwing IndexOutOfBoundException as given below: orca/orca-api/src/main/java/com/netflix/spinnaker/orca/api/pipeline/TaskResult.java:32: error: An unhandled exception was thrown by the Error Prone static analysis plugin. @builder ^ Please report this at https://github.com/google/error-prone/issues/new and include the following: error-prone version: 2.4.0 BugPattern: FallThrough Stack Trace: java.lang.IndexOutOfBoundsException at java.base/java.nio.HeapCharBuffer.subSequence(HeapCharBuffer.java:633) at java.base/java.nio.HeapCharBuffer.subSequence(HeapCharBuffer.java:41) at com.google.errorprone.bugpatterns.FallThrough.matchSwitch(FallThrough.java:70) at com.google.errorprone.scanner.ErrorProneScanner.processMatchers(ErrorProneScanner.java:451) at com.google.errorprone.scanner.ErrorProneScanner.visitSwitch(ErrorProneScanner.java:825) at com.google.errorprone.scanner.ErrorProneScanner.visitSwitch(ErrorProneScanner.java:152) at jdk.compiler/com.sun.tools.javac.tree.JCTree$JCSwitch.accept(JCTree.java:1229) at jdk.compiler/com.sun.source.util.TreePathScanner.scan(TreePathScanner.java:82) The error is due to Lombok lib upgrade from 1.18.16 to 1.18.20 as transitive dependency of Spring boot. Similar to the issue mentioned below google/error-prone#2575 Fix: To remove @Singular annotation from classes using Lombok builder pattern. Reference: projectlombok/lombok#2221
…#4194) * Remove @Singular annotation While building orca with upgraded spring boot version 2.3.12, google error-prone package throwing IndexOutOfBoundException as given below: orca/orca-api/src/main/java/com/netflix/spinnaker/orca/api/pipeline/TaskResult.java:32: error: An unhandled exception was thrown by the Error Prone static analysis plugin. @builder ^ Please report this at https://github.com/google/error-prone/issues/new and include the following: error-prone version: 2.4.0 BugPattern: FallThrough Stack Trace: java.lang.IndexOutOfBoundsException at java.base/java.nio.HeapCharBuffer.subSequence(HeapCharBuffer.java:633) at java.base/java.nio.HeapCharBuffer.subSequence(HeapCharBuffer.java:41) at com.google.errorprone.bugpatterns.FallThrough.matchSwitch(FallThrough.java:70) at com.google.errorprone.scanner.ErrorProneScanner.processMatchers(ErrorProneScanner.java:451) at com.google.errorprone.scanner.ErrorProneScanner.visitSwitch(ErrorProneScanner.java:825) at com.google.errorprone.scanner.ErrorProneScanner.visitSwitch(ErrorProneScanner.java:152) at jdk.compiler/com.sun.tools.javac.tree.JCTree$JCSwitch.accept(JCTree.java:1229) at jdk.compiler/com.sun.source.util.TreePathScanner.scan(TreePathScanner.java:82) The error is due to Lombok lib upgrade from 1.18.16 to 1.18.20 as transitive dependency of Spring boot. Similar to the issue mentioned below google/error-prone#2575 Fix: To remove @Singular annotation from classes using Lombok builder pattern. Reference: projectlombok/lombok#2221 * Revert "Remove @Singular annotation" This reverts commit e0d57a2. * Suppress warning for "FallThrough" bug pattern reported by errorprone While building orca with upgraded spring boot version 2.3.12, google error-prone package throwing IndexOutOfBoundException as given below: orca/orca-api/src/main/java/com/netflix/spinnaker/orca/api/pipeline/TaskResult.java:32: error: An unhandled exception was thrown by the Error Prone static analysis plugin. @builder ^ Please report this at https://github.com/google/error-prone/issues/new and include the following: error-prone version: 2.4.0 BugPattern: FallThrough Stack Trace: java.lang.IndexOutOfBoundsException at java.base/java.nio.HeapCharBuffer.subSequence(HeapCharBuffer.java:633) at java.base/java.nio.HeapCharBuffer.subSequence(HeapCharBuffer.java:41) at com.google.errorprone.bugpatterns.FallThrough.matchSwitch(FallThrough.java:70) at com.google.errorprone.scanner.ErrorProneScanner.processMatchers(ErrorProneScanner.java:451) at com.google.errorprone.scanner.ErrorProneScanner.visitSwitch(ErrorProneScanner.java:825) at com.google.errorprone.scanner.ErrorProneScanner.visitSwitch(ErrorProneScanner.java:152) at jdk.compiler/com.sun.tools.javac.tree.JCTree$JCSwitch.accept(JCTree.java:1229) at jdk.compiler/com.sun.source.util.TreePathScanner.scan(TreePathScanner.java:82) The error is due to Lombok lib upgrade from 1.18.16 to 1.18.20 as transitive dependency of Spring boot. Similar to the issue mentioned below google/error-prone#2575 Further investigation reveal that bug pattern "FallThrough" for lombok annotations (like @Singular) could be a false positive, because Lombok modifies the AST on the fly causing trouble to errorprone checks, as mentioned in below issue and PR. google/error-prone#613 google/error-prone#1573 Considering the above points, we can suppress warning of "FallThrough" bug pattern reported by errorprone for @Builder/@Singular annotations.
The @Singular annotation is an amazingly powerful feature, and it's great especially for dealing with test code where we have to construct objects that contain maps and lists. However, we have use cases where it's appropriate (and sometimes required) that a collection be explicitly null, and the @Singular annotation makes that impossible currently.
We ran into a severe regression in a part of our code where we made use of
@Singular
to optimize tests that otherwise had to go through a lot more work to create collections and pass them in to the builder. Because@Singular
causes the "plural" form of the collection builder method ("aliases" above) to rejectnull
, a part of our code had to be updated to substitute an empty collection, and this triggered a bug in some older deployments of our environments that handled the empty collection differently fromnull
. Since we cannot easily go and patch those environments to fix the bug, we have to stop using@Singular
on the class in question and go back to manually constructing collections in the test code.It should be possible to explicitly set a
@Singular
collection tonull
in the builder and getnull
as the result, with appropriate error handling for if you then try to call the singularized method. E.g.:The text was updated successfully, but these errors were encountered: