Chris said:
Or simply -- use the least specialized type that fits.
In this case, though, Patricia /can/ use List, since it supports all the
necessary operations. ArrayList would be overspecifying (or might be). The
information that is, or isn't, being communicated isn't really type-related[*]
at all, but is a quality-of-service aspect. It isn't enough just to be able to
do <something>, but the object must be able to do <something> with certain
performance guarantees.
[*] "type" here being construed in the sense of Java's type system. It's
possible to imagine a type system where asymptotic performance bounds were part
of methods' type signatures.
Agreed. However, I see no problem with type system defined like that.
A lot of (less or more useful) "typologies" coexists in Java's type
system (are they all, for example Serializable semantics, really
type-related?), why then not to add that another one (i.e. performance
aspect) too? Especially when that matters, and may become a source of
not only poor quality of service, but even of denial of service (e.g.
system not responding in "reasonable" time...).
That type can be simply defined (the IndexedList from your previous post
is an example). But it holds obvious disadvantage, classes like
ArralList must additionally implement it to become useful.
We can also define that type "virtually" using type intersection --
which I can't point out important disadvantages for.
What's then the argument against using intersection type here?
BTW -- I've just explored a bit deeper my advice from previous post, and
it results in conclusion, that intersection type can be used even
simpler in that case, e.g. that way:
<L extends List<?> & RandomAccess> void processList(L list)
or, of course, when a concrete list elements type is know, that way:
<L extends List<String> & RandomAccess> void processList(L list)
Nevertheless, my previous parameterization with two type variables, may
still appear useful when we need additional constrains on list's element
types, or when exact return type of list is unknown, and we don't want
to lose any information about it from method's return type, e.g.:
<E, L extends List<E> & RandomAccess> L filter(L list) {
// ...
return list;
}
Although, usage of such a method is probably slightly limited (instead
of method-chaining and the like), it's /at least/ worth to be noted as
an alternative declaration.
Inconvenience here is when we want to have, for example, a generic
factory method like that:
<E, L extends List<E> & RandomAccess> L createRandomAccessList() {
return (L) new ArrayList<E>(); // unchecked cast
}
Which, despite of that ugly unchecked cast, seems to be useful at first,
and it even works as expected, but holds a small problem... We can not
directly pass its result as an input for another method which it should
be compatible with -- the following won't compile:
processList(createRandomAccessList());
That's because type inference takes place here, so without any
additional advice compiler fails with that.
Hopeless, while Java generics is implemented using type erasure (and
capture conversion, and so on...), we (I) can't even workaround it, i.e.
to make it working without explicitly provided type argument...
But let's beck to the original problem...
Don't you think, that in Patricia's particular case, /possibly/ neither
the use of List nor ArrayList is better than using intersection type of
List and RandomAccess?
piotr