Suppose some capsule has a procedure of the form Point ones()
, where Point
is a Java class defined
public class Point {
public int x;
public int y;
public void incX() {
x++;
}
}
and ones()
is defined
Point ones() {
Point rv = new Point();
rv.x = 1;
rv.y = 1;
}
This will trigger @PaninJ to create a duck which extends Point
. This duck, any duck which extends Point
, and any other duck which extends a class with public fields are all unsafe in three ways:
- Data Consistency.
- Data Races.
- Incorrect Aliases
In both @PaninJ and panc
, ducks extend a class in such a way that the user-defined methods of the duck delegates to a "results" object (i.e. the object with which the duck is resolved). This delegation strategy is safe when all user code interacts with the duck's methods: the object's data is kept consistent by consistently delegating to the encapsulated object and race conditions are avoided by blocking on all method calls until the object has been resolved.
However, by introducing public fields, a user can modify the fields of a duck without also modifying the state of the encapsulated results object. In the Point example, there are two copies of the two fields.
- The coordinates on the duck itself are what the user will be interacting with when accessing the duck's fields.
- The coordinates on the encapsulated results object are what the user will be interacting with whenever using the duck's methods.
These two sets of fields are not going to be consistent unless we implement a solution which keeps them consistent. We could implement a solution which copies each of the fields of the results object to the duck's fields when the duck is resolved. We could even implement a solution which syncs the two sets of fields at the entrance and exit of the duck's methods.
However, implementing these synchronization mechanisms will not solve two important problems:
- Data Races: The user is able to read from and write to the duck's fields before that duck has even been resolved.
- Incorrect Aliases: Some Java objects in the system may refer to the encapsulated object when they should be pointing to the duck. What would be the correct synchronization behavior if a
Point
's x
coordinate was changed on both the duck and the encapsulated result? I think that the most recent change should be used. However, there would be no a way of knowing which one was modified most recently.
This idea of trying to synchronize the fields of two objects just can't work when the user is allowed to write to fields. Well, you might say that the fields need be declared final
, however this solution won't work either, because if the duck's fields are final, then they cannot be updated once the duck has been resolved.
All of this seems to lead me to the conclusion that there is no way to safely allow non-private fields on a duck when the delegation strategy is used.