Good day. Thank you for an awesome library! I apologize if this question was asked before.
I am curious why for the next class:
@Value.Immutable
public interface Person {
String getName();
List<String> getEmails();
}
ImmutablePerson has:
@Override
public ImmutableList<String> getEmails() { // Why ImmutableList and not simply List?
return emails;
}
I know that guava implementation for List / Set / Map is used if guava is available on the classpath. My question is for return type of getter. Will it make sense to use the same type that is declared on interface?
Thank you.
P.S. jdkStyle = true will fix that by not using guava's collections at all.
I dunno how to explain it, you might ask "why", I would think "why not" ... It is a covariant return type override, providing more precise information. In general, a lot of [java] programmers have too much buy-in for the using "as abstract type as possible". Some use "Collection" when then they essentially work with "List" or "Set" and programmer know it, but prefer not to express. The abstraction process is discarding some amount of information as non-essential and generalizing the rest. Then, the process of deciding what is essential and what is not sometimes can yield strange unexpected conclusions. When try to abstracting away collection type, one could say: "it's only a collection, you don't need to know anything else", but then there's the contradiction "ok, but it's a list, and not a regular list, but a special kind, on which add/remove methods prohibited so you cannot use it just as list or collection, you'll lose important information". So there's often a dilemma, is abstracting something is a good idea, is having less information is always better than having more, more specific information. Sometimes I think about it as together with less information on something in addition you have to carry more entropy about something that could be useful info, but you've omitted it and now you're carrying more indeterminacy together ("leaky abstractions" and all the stuff).
Ok, there was a lot of fluff, but practically speaking, I would say that in the initial design we were following the idea "Immutables is just like Guava's Immutable collections but for regular objects", and we tried to mimic all the sorts of conventions Guava used. For example, you have Map and it has Set keySet(), if you got ImmutableMap it has ImmutableSet keySet() visible to you, so you kind of propagate that immutability information, and add/remove methods are deprecated so IDE warns you if you accidentally use it, yada yada etc etc. So if we have Person with List friends() Immutables generator will generate ImmutablePerson with ImmutableList friends() attribute, just following the convention. In practice people can use ImmutableObj pattern as a way of learning the concept (piggy-backing on the familiarity with Guava etc, if there is any). Then a lot of users of Immutables are switching to the "inverse"-generation (also called "reverse" sometimes) style AbstractPerson -> Person where simple name is used for immutable class, not for an abstract value type. The other form of using a simple name is "sandwich"-generation style. Where only abstract types are exposed, which is somewhat similar to AutoValue+FreeBuilder approach hybridization). interface Person { ... class Builder extends ImmutablePerson.Builder{} }, so you use new Person.Builder().aaaa(a).bbb(b).build() and never see Immutable prefixes from where you use it.
I hope it helps, thanks!
For the record this "feature" actually breaks things pretty often. Imo @Style(jdkOnly) behavior should be default. You might only have guava on the classpath via some transitive dependency but suddenly Spring Boot starts mysteriously returning 415s or your swagger specs have a phantom "empty" attribute on your objects.
For now I'll just use custom styles.
our OOP in Java (and other langs) is broken in general, Barbara Liskov crying. (and I'm not even going into OOP itself is broken :-p ). For the sake of "backward" compatibility, we would probably, not change the defaults, but if switching jdkOnly works - that's great. In addition, you can "inhibit" Guava discovery via META-INF extensions (can look up issues here).
Of course, we are not serving well java 9+ users, who would benefit if we just use standard immutable collection and so we will be past all this "Guava nonsense". I want to get to it sometime, but currently very busy with other stuff.
Just as a matter of my own professional career, I also stopped using any Spring, Boot, Jackson binding (only use streaming part of Jackson), and of course, no Hibernate-likes for many years now, their complexity contributes to all those WTF moments and annoying bugs - not suggesting ditching any tech to anyone, but just sharing my practices. And unfortunately, _Immutables_ is also contributing to those kinds of complexity of the Java ecosystem, so I feel guilty too ;-)
Completely agree - the fact that jackson can't tell me at compile time my encoder is wrong/doesn't exist is very disappointing. These mainstream libraries turn Java into a language with the worst of dynamic and static typing.
Anyway, thanks for your work on this library, even if I can't have compile time json encoders in java at least I can basically have case classes.