JEP 186: Collection Literals
Remi Forax
forax at univ-mlv.fr
Fri Jan 31 00:43:53 PST 2014
Hi Nick,
let me explain why you can *not* have an interface that represent an
immutable type in Java.
In Java, immutability is a property of a class not an interface, here is
why:
Lets think a little bit about why class java.lang.String is declared final.
It's because otherwise some can write:
class MutableString extends String {
int shorter;
int length() { return super.length() - shorter; }
}
because inheritance implies subtyping, it means that now, a constructor
that takes a String
as parameter don't know if it has to do a defensive copy or not:
class IAmMutableMaybe {
final String s;
IAmMutableMaybe(String s) { this.s = s }
}
by example this code break encapsulation:
MutableString ms = new MutableString();
IAmMutableMaybe iamm = new IAmMutableMaybe(ms);
ms.shorter = 2; // here I change the behavior of iamm withtout calling
a method of IAmMutableMaybe
so an immutable class must be final.
because in Java, anybody can implement a public interface ; an interface
doesn't list its implementations ;
you can not guarantee that all implementation of an interface will be final,
so you can not represent an immutable type by an interface in Java.
If one day the JDK has persistent collections (and I hope it will),
there will be not interface to abstract them.
(you can still ask all implementations in the javadoc of the interface
to super swear that they are immutable but it's a weak contract).
regards,
Rémi
On 01/30/2014 04:13 PM, Nick Williams wrote:
> I've seen a lot of talk on mutable vs. immutable literals. I think in must cases users want their literals to generate immutable collections. However, I don't think this is an absolute rule. I also think it's an easy problem to solve. For that matter, we could also solve something that has always bothered me about Java collections: every interface is mutable. Why even LET someone call an add/update method and get a runtime exception? Wouldn't it be better to never supply an add/update method in the interface? Even better: We can implement this WITHOUT breaking binary compatibility! Observe:
>
> ImmutableIterator
> Iterator
>
> ImmutableIterable
> |
> |-->ImmutableCollection
> | |
> | |-->ImmutableList----------|
> | | |
> | |-->ImmutableSet--------| |
> | | | |
> | |-->ImmutableQueue---| | |
> | | | |
> |-->Iterable | | |
> | | | |
> |-->Collection | | |
> | | | |
> |-->List<----)--)--|
> | | |
> |-->Set<-----)--|
> | |
> |-->Queue<---|
>
> ImmutableIterable (cont'd)
> |
> |-->ImmutableMap--------|
> | |
> |-->Iterable (cont'd) |
> | |
> |-->Map<--------|
>
> Then you just need to move all the mutating methods of each interface into its immutable superinterface. Voila! It doesn't break any existing code, but you can now use immutable collections:
>
> ImmutableList<String> list = ["hello", "world"]; //immutable
> List<String> list = ["hello", "world"]; //mutable
>
> ImmutableMap<String, String> map = { "hello":"world", "foo":"bar" }; //immutable
> Map<String, String> map = { "hello":"world", "foo":"bar" }; //mutable
>
> Nick
>
> On Jan 23, 2014, at 6:58 PM, Remi Forax wrote:
>
>> On 01/23/2014 09:53 PM, Zhong Yu wrote:
>>> I think we should almost*never* declare a weaker type on
>>> non-publicized variables, like
>>> List<Thing> things = new ArrayList<>();
>>> instead, we should almost always declare the most specific type, like
>>> ArrayList<Thing> things = new ArrayList<>();
>>> I don't want to start an argument about this issue here;
>> I agree.
>>
>> The main issue is with a code like this
>> List<Thing> things = new ArrayList<>();
>> for(int i=0; i< things.size(); i++) {
>> ... // i is used here
>> }
>>
>> because it can be changed to the code below without any warnings
>> List<Thing> things = new LinkedList<>();
>> for(int i=0; i< things.size(); i++) {
>> ... // i is used here
>> }
>>
>> The complexity of the first code is O(n) the second one is O(n2).
>>
>> cheers,
>> Rémi
>>
>>
More information about the lambda-dev
mailing list