Sometimes you need to control which fields are included in the toString method to avoid information leaking to logs. Lombok does this via the @ToString(includeFieldNames={}) annotation.
You might want to take a look at Opaque containers or customizing toString just take a look on the guide maybe you find your desired behavior already possible..
I am familiar with those sections of the documentation. I am in the situation where I need most fields to be excluded from toString, but all fields included in the equals and hashcode. My solution so far is to manually write a toString method. This is effective but would be easier if I could just list the fields I wanted to be in the toString ( as Lombok allows). Opaque containers also requires the user to manually write a toString. I definitely see this as a feature request and not any kind of bug.
Thank you for the feature request! Clearly, lombok's solution doesn't cut in as it relies on strings which would not be verifiable by compiler: think of when renaming attributes, or adding new ones.
I somewhat the dismissal the "opaque container" approach. It's absolutely not an issue that you will have to manually write very simple toString once. The power comes from the proper data modeling and the benefits which come from it. Haven't people tired of stringly-typed programming (in addition to intly and booleanly)? In the wild, emails, names, credit card numbers, everything being represented as String. When you create proper types for those, compiler can check types, you will not put email to name field or credit card number to description field by accident. A compiler will check it. As an example, if you create special type for a credit card, you can not only add some useful methods to that class (like validations, get type of card, etc), easily find usages of the type in a codebase, but you can override toString (note: only once!) to produce redacted string, so no loggers or println statements will accidentally output credit card number alone or as part of any other objects' toString. Isn't it a valid assumption that if you want to hide some data from the toString of one class, you will also try to hide it if you put the same data into other classes?
While it's not a bad idea to implement something like@Redacted annotation (one of AutoValue's extension), I just don't see it can be useful as "opaque containers" provide a powerful, fully-customizable and sufficient solution. Adding single class and creating additional object is negligible and given sensitivity of a data it's quite reasonable.
// define once, use everywhere
@Immutable abstract class HiddenValue {
@Parameter abstract String value();
public String toString() {
return "#########(REDACTED)";
}
public static HiddenValue of(String value) {
return ImmutableHiddenValue.of(value);
}
}
@Immutable interface AnyOtherObject {
int a();
HiddenValue hidden();
}
// to string: AnyOtherObject{a=1, hidden=#########(REDACTED)}
If I were to write the interface and implementation of this type of class by hand, I can't see myself ever choosing to wrap a String or other object in a container just to change how toString() is implemented. Interfaces are important, and I don't think they should be made obtuse unnecessarily. At that point, it's probably better to generate the code once, fix it up, then check it in.
Opaque container objects seem like the best story available in Immutables today, but I think Immutables would be even better with a solution that:
toString())I find your argument about "credit card number" objects pretty compelling, but not quite compelling enough to get over how this change to my interface has a non-orthogonal impact on e.g. how my objects are serialized, causing me to write a Jackson serializer / deserializer for the opaque container.
I don't have an opinion on how functionality should be added to solve this, but right now it can lead to square-peg / round-hole situations.
Haven't people tired of stringly-typed programming
Man, I would love if I could define types in Java like public class Name aliases String, whose sole purpose in life is to prevent things like assigning a name to a credit card number and otherwise allows no extension, but I don't think I'm going to get much buy-in to wrapping every primitive and String with an object. I think you could do this kind of thing at compile time with a pluggable type checker (e.g. JSR308), but I don't see people doing it.
I'd like to +1 something like @Redacted mentioned above or @ExcludeFromToString to specify attributes I'd want out of my toString() method.
My use case is: I have an @Immutable.Value interface that has a byte[] getBytes() method. If I use the "opaque container" pattern and create a ByteArrayWrapper with a custom toString() I can make it work, but I need to play around JSON serialization issues now and I do not think it's worth.
I am currently going the "write custom toString()" method for my interface (turned into an) abstract class, which is indeed not much work. But I'd appreciate being able to just annotate and move on with my life :)
Thanks!
EDIT: In order to be able to write a custom toString() I moved from an interface to an abstract class.
due to the popular demand @Value.Redacted was released in 2.5.0
Most helpful comment
due to the popular demand
@Value.Redactedwas released in 2.5.0