RFR: 8180352: Add Stream.toList() method [v2]
Peter Levart
plevart at openjdk.java.net
Sun Nov 8 16:00:56 UTC 2020
On Sun, 8 Nov 2020 10:47:08 GMT, Peter Levart <plevart at 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
More information about the core-libs-dev
mailing list