Frage

During my university classes on Java I learned about the concept of a privacy leak, where a public getter returns a reference to a private mutable object.

The example they gave was as follows: (Forgive me if the syntax isn't quite right, I'm working from memory)

private HashSet<*> priv = new HashSet<*>

public Collection<*> getPriv () {
    return this.priv;
}

Now the problem with this is the object's client now has the opportunity to corrupt the private HashSet, intentionally or otherwise.

On the course, the suggested solution was to use the following:

public Collection<*> getPriv () {
    return Collections.unmodifiableCollection (this.priv);
}

This seems like a good solution to me, because not only is the internal state of the object protected from external modification, any attempt to modify the returned unmodifiableCollection will trigger an exception. The problem with this though, is that it only works with collections. There doesn't seem to be equivalents for other mutable Java classes.

Most books on Java I've read since suggest the following:

public Collection<*> getPriv () {
    return this.priv.clone ();
}

This works on any clonable object, but the returned object can be modified to the client object's content. The internal state of the object is still protected, but now I don't get an exception if I try to modify data that can't really be modified. This means I could make a mistake where I get the private collection, modify it, and am left mystified as to why my changes aren't reflected in the object, so I've swapped one class of errors (modifying private state) for another (modifying a clone that should be immutable).

Is there a more generalized way of doing return Collections.unmodifiableCollection (this.priv); that will work with any class (or at least any class that meets some prerequisite such as implementing cloneable), or is my only option to implement mutable and immutable versions of all the private classes I might want to make publicly available through a getter?

War es hilfreich?

Lösung

I'm afraid the answer is yes.

Note that Collections.unmodifiableCollection only returns a view. Essentially, Collections contains an adapter class which takes in any Collection and forwards certain method calls to it. For the 'dangerous' methods, it throws an exception instead. This relies on the underlying collection passed in to implement things in a sane manner of course. If getSize() mutates the object, then calling getSize() on the adapter will still forward it to the underlying class, which is then modified.

You can reduce the amount of work by limiting the number of different classes (or interfaces) you return to clients. You can also make more of your classes immutable by default, a common paradigm in functional languages which makes things much simpler. Unfortunately, Java makes working with immutable objects unnecessarily ugly.

There are also possibilities like generating the immutable views automatically, but that's complicated and requires you to have a way of deciding which methods to forward anyway.

Lastly, none of this protects you from setAccessible or the like. But in that case, they're deliberately violating the standard VM constraints, so it's not really something you should worry about. If you're worried about security, you'll need to run all untrusted code in a SecurityManager anyway.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top