On Sun, 8 Nov 2020 10:47:08 GMT, Peter Levart <plev...@openjdk.org> wrote:
>>> Simon Roberts wrote: >> >>> This discussion of unmodifiable lists brings me back to the thought that >>> there would be good client-side reasons for inserting an UnmodifiableList >>> interface as a parent of LIst, not least because all our unmodifiable >>> variants from the Collections.unmodifiableList proxy onward fail the Liskov >>> substitution test for actually "being contract-fulfilling Lists". >> >> At some point there probably will need to be a long article explaining all >> the issues here, but at the moment the best writeup I have is this one: >> >> https://stackoverflow.com/a/57926310/1441122 >> >> TL;DR there are a few different ways to approach retrofitting something like >> this, but they all have enough compromises that the net benefits are unclear. > > Hi Stuart, > > I would like to discuss the serialization. You introduce new > CollSer.IMM_LIST_NULLS type of immutable collection. This means that if this > change goes into JDK16 for example, JDK15 and before will not be able to > deserialize such list as they know nothing about IMM_LIST_NULLS even if such > lists don't contain nulls. The reason you say to chose new type of > serialization format is the following: > >> "Suppose I had an application that created a data structure that used lists >> from List.of(), and I had a global assertion over that structure that it >> contained no nulls. Further suppose that I serialized and deserizalized this >> structure. I'd want that assertion to be preserved after deserialization. If >> another app (or a future version of this app) created the structure using >> Stream.to >> List(), this would allow nulls to leak into that structure and violate that >> assertion. Therefore, the result of Stream.toList() should not be >> serialization-compatible with List.of() et. al. That's why there's the new >> IMM_LIST_NULLS tag in the serial format" > > I don't quite get this reasoning. Let's try to decompose the reasoning giving > an example. Suppose we had the following data structure: > > public class Names implements Serializable { > private final List<String> names; > Names(List<String> names) { > this.names = names; > } > public List<String> names() { return names; } > } > > App v1 creates such structures using new Names(List.of(...)) and > serializes/deserializes them. They keep the invariant that no nulls are > present. Now comes App v2 that starts using new Names(stream.toList()) which > allows nulls to be present. When such Names instance from app v2 is > serialized and then deserialized in app v1, nulls "leak" into data structure > of app v1 that does not expect them. > > But the question is how does having a separate CollSer.IMM_LIST_NULLS type > prevent that from happening? I can see that having a separate IMM_LIST_NULLS type might be necessary to preserve the allows-null/disallows-null behaviour of indexOf and lastIndexOf methods... NOTE ALSO that while ListN.equals(o) method is using Objects.equals(o1, o2) to compare elements, hashCode is inherited from AbstractImmutableList: public int hashCode() { int hash = 1; for (int i = 0, s = size(); i < s; i++) { hash = 31 * hash + get(i).hashCode(); } return hash; } ...which means it will throw NPE when the list contains null. The same goes for SubList. ------------- PR: https://git.openjdk.java.net/jdk/pull/1026