Skip to main content

"fixed" as a complement to "final"

19 replies [Last post]
jsbean
Offline
Joined: 2005-02-15

The keyword "final" makes sure that a primitive is not changed after its initialization. I would like a similar keyword, "fixed", that could be used for both primitives and objects with the semantics "will not change".

Examples:

public fixed int[] numbers = new int[] {1, 2, 3}
// numbers will remain {1, 2, 3} throughout its lifetime

public fixed Vector getChildren() {
return myChildren;
}
// any caller of the method has to declare the variable that is initialized to the result as fixed, so the author of the method knows that no caller will be able to change the collection that is returned. Note that myChildren doesn't need to be fixed just because the return-value of the method is.

I think that this would be a neat way to make sure that Collections and other objects that are returned by public methods won't change from the outside.

The fixed keyword could be checked in compile-time, so no changes would be made to the bytecode. This feature demands that the compiler can analyze which parameters could possibly be changed within a method and tag parameters accordingly.

Ex.

void doSomething(Vector children) {...}
...
fixed children = node.getChildren()
doSomething(children)
// this will give a compile-error unless the compiler can make sure that the parameter "children" won't change within doSomething

Reply viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.
jonbarril
Offline
Joined: 2004-12-15

Take a look at thread...

http://forums.java.net/jive/thread.jspa?forumID=23&threadID=283

Wrapper classes are a good way to achieve defensive programming, as encouraged by Bloch. I have used it liberally to provide singleton unmodifiable views of an object or a part of its state in a framework where the client is distinct from the implementor (public-personal modifiers generally do not allow such a fine distinction).

Also, with a variation on the wrapper class theme (see Wrapper and Wrappable below) it is possible to make the wrapper target test equal to the wrapper object -- something that an immutable can't do. The problem with this approach in its fullest and most robust (defensive) form is that a final method is too final (hence the suggestion for semifinal).

--jon

/**
* An interface indicating that this object can be wrapped by a Wrapper,
* as in a wrapper chain, with equality (hashCode(), equals()) of the
* Wrapper being that of this Wrappable. This behavior allows a Wrapper
* to be safely used interchangeably with its Wrappable wrapper target
* in a collection.
*


* Objects whose equality must remain distinct from that of its wrapper
* should not implement this interface. As such, a non-Wrappable cannot
* be wrapped by a Wrapper, but this does not preclude a non-Wrappable
* object from testing equal to a Wrapper object (as in a Set wrapper
* testing equal to any Set).
*
* @see Wrapper
* @see AbstractWrapper
*
* @author Jon Barrilleaux (jonb@jmbaai.com) of JMB and Associates Inc.
* @version $Revision: 1.1 $
*/
public interface Wrappable {}

/**
* An interface allowing this object to wrap a Wrappable target object,
* as in a wrapper chain, with equality (hashCode(), equals()) of this
* Wrapper being that of the Wrappable. Although the target of a Wrapper
* must be either null or a Wrappable, this does not preclude a non-Wrappable
* object from testing equal to a Wrapper object (such as a Set wrapper
* testing equal to any Set).
*


* A given wrapper object can act as either a "wrapper" or as an "implementor".
* In a wrapper chain, if a wrapper's target object (next link in the wrapper
* chain) is not null, the wrapper is wrapping, not implementing. In a
* wrapping wrapper, all non-private methods should forward implemention
* to the wrapper target, after possibly decorating the method behavior. If
* a wrapper's target is null, the wrapper is not wrapping, but implementing.
* An implementor is the last link in the wrapper chain since there is no
* target for it to forward implementation to.
*


* The process whereby methods forward to the wrapper target or implement
* the method according to the presence of a non-null wrapper target is
* called "wrapper resolution". A distinguishing feature of a Wrapper is
* that equality is ultimately based on the wrapper chain implementor. As
* such, when testing two object wrappers for equality, both the "this" and
* the "other" object must be resolved down to their implementors before
* performing the equality test. For similar reasons, a wrapper method
* must assume that any parameter can itself be a wrapper, which requires
* wrapper resolution. As such, a wrapper method must never access the
* state variables of a parameter directly. Instead, the method must always
* access the parameter state via its methods so that wrapper resolution
* will be performed.
*


* Typically, the target of a wrapper is meant to be hidden from the client
* so that the client can only access the target via the wrapper. To assure
* that the wrapper target remains hidden a wrapper must never expose the
* target, and a wrappable must never expose itself, to a foreign object,
* such as a global data monitor or a navigable relation.
*


* The following is an implementation example for a class that implements
* Wrapper.
*

 *	private Wrappable _target;
 * 
 *	public final int hashCode() {
 *		// if this is wrapping, forward to wrapper target
 *		if(hasWrapperTarget()) {return _target.hashCode();}
 *
 *		// this is impling, do it
 *		...compute hash code
 *		return code;
 *	}
 *
 *	public final boolean equals(Object obj) {
 *		if(obj == this) return true; // shortcut
 *		
 *		if(WrapperUtils.isImplementor(this)) {
 *			// this is impling: use this, resolve obj
 *			if(WrapperUtils.isImplementor(obj)) {
 *				// both are impling: do it
 *				...test if this is equal to obj
 *				return isEqual;
 *			} else {
 *				 // obj is wrapping: delegate to obj
 *				return obj.equals(this);
 *			}
 *		} else {
 *			// this is wrapping: use target, resolve obj
 *			if(WrapperUtils.isImplementor(obj)) {
 *				 // obj is impling: forward to target
 *				return _target.equals(obj);
 *			} else {
 *				 // obj is wrapping: delegate to obj
 *				return obj.equals(_target);
 *			}
 *     }
 * }
 * 

*
* @see Wrappable
* @see AbstractWrapper
*
* @author Jon Barrilleaux (jonb@jmbaai.com) of JMB and Associates Inc.
* @version $Revision: 1.1 $
*/
public interface Wrapper extends Wrappable {

/**
* Returns true if this object has a non-null wrapper target,
* which means that this object is acting as a wrapper and not as
* an implementor. By definition, the wrapper target must be a
* Wrappable.
* @return The result.
*/
public boolean hasWrapperTarget();

/**
* If this object has a target, forwards to it, otherwise computes
* and returns the hash code. Recurses as needed until a wrapper chain
* implementor is found.
*/
public int hashCode();

/**
* If the other object is not a Wrappable, returns false. If this
* and the other object are implementors, tests their equality;
* otherwise, this and the other object must be resolved to their
* implementors by forwarding, delegating, and recursing as needed
* (see example in class javadoc).
* @param obj Reference to the other object. If null, returns false.
* @return True if the implementor of this and the other object are
* equal.
*/
public boolean equals(Object obj);

}

hlovatt
Offline
Joined: 2003-11-18

> Take a look at thread...
>
> http://forums.java.net/jive/thread.jspa?forumID=23&thr
> eadID=283
>
> ...
>
> Also, with a variation on the wrapper class theme
> (see Wrapper and Wrappable below) it is possible to
> make the wrapper target test equal to the wrapper
> object -- something that an immutable can't do.

You can make Immutables test equal to their Value counterparts, see Integer example in the thread you reference. You simply make the Value and Immutable classes inherit from a common base and put the equality test in the common base.

jonbarril
Offline
Joined: 2004-12-15

By definition an immutable object has a state that is unchanging. As such, an immutable object that clones the state of a mutable object can only be equal to the mutable object until the mutable object's state changes. An unmodifiable view of a mutable object that implements the Wrapper interface will always test equal to the wrapper target.

Am I missing something? Could it be that you are confusing immutable and unmodifiable.

--jon

hlovatt
Offline
Joined: 2003-11-18

Yes you are right - I misunderstood what you wanted. I thought you simply wanted to put immutable types and value types in the same Collection.

hlovatt
Offline
Joined: 2003-11-18

In reply to markf,

> I understand that Gosling himself has proposed a system
> for const references.

His proposal is for immutables:

http://today.java.net/jag/old/FP.html

> I should of course note that const is not a replacement
> for immutable objects nor vice versa (const applies to
> references, immutable to objects).

When you have immutable you don't need const references, see below for an example. The example below uses the notation from:

https://pec.dev.java.net/nonav/compile/javadoc/pec/compile/immutable/Imm...

A Value type has value semantics but is mutable and an Immutable also has value semantics but is immutable. Value semantics are when equals, hashCode, and toString are based on the value of fields instead of the objects memory address. The abstract base class is a const reference.

e.g. instead of:

ValueInteger vi = new ValueInteger( 1 ); // mutable
mutateValue( vi ); // changes value of vi
immutableValue( vi ); // doesn't change value of vi - argument declared as AbstractInteger

Do (just showing last line - it is the only one that changes):

immutableValue( (ImmutableInteger) vi.toImmutable() ); // doesn't change value of vi - argument declared as ImmutableInteger

If the creation of temps is too slow, then cache it.

This caching normally invites refactoring of code into separate concerns, typically a model-view-controller type structure with the controller containing a private mutable model and giving multiple views the cached immutable.

This separation of concerns is one of the many advantages of immutables, it encourages good code organization whereas const reference doesn't.

Also see: Joshua Bloch, [i]Effective Java[/i], "Favor Immutability".

markf
Offline
Joined: 2005-01-20

http://today.java.net/jag/old/FP.html

I read this over; I like their proposal (I can't stand the idea of immutability through an interface though; it's hackish. See "Cloneable" for details). All they're missing is Tuples to have a really great new language.

It still leaves open the problem of const correctness though. Often with a large, mutable class, it's desirable to explicitly state that certain methods do not alter the object's state. This has potent implications for multithreaded behaviour, and for validation. The problem highlighted by lucretius2 is indeed a troubling one, but one that exists completely outside of immutability. Whenever class x has a method that takes another member of class x (or any superclass or subclass), it has to check that it's not operating on itself. Const correctness doesn't eliminate this problem, nor does immutability. It's just a fact of life when you deal with methods like this.

Consider the case where you are dealing with extremely large mutable objects that take each other as parameters. Duplicating them for the purposes of passing them as immutable parameters isn't realistic. This is also a problem with graphs of connected objects. Duplicating an object either buys you nothing (because it's still connected to mutable objects), or is expensive (because you're duplicating the entire graph). But const lets you be fairly sure of what you're doing. You can hold a const reference to the large object, and a const reference to the graph node can only be used to acquire const references other graph nodes (unless the fields to them are declared "mutable").

Yes, there are still some gotchas as lucretius2 pointed out; but they're gotchas that exist regardless of const correctness or immutability.

hlovatt
Offline
Joined: 2003-11-18

There are some complelling advantages of using an interface to mark immutability, e.g. x instanceof Immutable and List< Immutable >.

I find that if you use Immutables first off it is rare that you need const references just use a const interface. If you use immutables you will rarely need const references.

Immutables do in practice solve many of the const aliasing problems, so problems like that highlighted by lucretius2 simply don't exist.

markf
Offline
Joined: 2005-01-20

I'm curious how an immutable interface would work? Is it an immutable wrapper class, like the ones that Collections.unmodifiableXX uses? That doesn't really solve the aliasing problem, because the original object is still mutable; in fact, it makes the problem lucretius2 mentioned even more difficult to detect, because now the == operator can't detect that the objects are the same.

hlovatt
Offline
Joined: 2003-11-18

I have implemented a compiler, called a Pattern Enforcing Compiler (PEC), that uses Immutable as a marker interface:

https://pec.dev.java.net/nonav/compile/index.html

Particularly see description of Immutable:

https://pec.dev.java.net/nonav/compile/javadoc/pec/compile/immutable/pac...

It pretty much follows the RFE for Immutables:

http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4617197

To the detail of you question, the immutable marker doesn't use a wrapper it is a separate class and therefore immune from aliasing problems. You can hand code immutable classes in Java, what the PEC adds is that the compiler checks that the class you have written really is immutable.

markf
Offline
Joined: 2005-01-20

A system for const references also demands a system for const methods. A const (or "fixed") method can't make any changes to the state of its object, nor can it call any other non-const methods. If you are holding a const reference, you can only invoke const methods, and you can only copy the reference to other const references.

It's entirely possible to build a very clean, well-behaved system. It require several basic elements:

1.) Const references and fields.
2.) Const methods.
3.) Mutable references and fields.

Mutable fields are special fields that are allowed to change even in a const method. They are used for things like caching results. They aren't considered part of the Object's state.

There are a couple of really good papers about this, and I understand that Gosling himself has proposed a system for const references.

I should of course note that const is not a replacement for immutable objects nor vice versa (const applies to references, immutable to objects). They are orthogonal concerns, not complementary or contradictory.

lucretius2
Offline
Joined: 2004-12-19

Const references are relatively simple to add to the language.

Unfortunately they're not that useful. Since (as pointed out above) you can have both const and non-const references to the same object, having a const reference to an object doesn't guarantee that it won't change its state. In fact, due to multithreading, an object accessed through a const reference can change its state [i]while a method is using it[/i].

There is also scope for subtle bugs. Suppose you have an add() method like this:

class MyNumber {
MyNumber add(const MyNumber other) {
MyNumber result;
// blah blah
return result;
}
// etc.
}

add() adds number 'other' to 'this'. It looks like the object 'other' can't change during this method: it has a const reference so you can't change it. But there is a subtle bug: suppose you try to add a number to itself? Then both 'this' and 'other' refer to the same object, via a non-const and a const reference. When you change the object 'this', the object 'other' changes behind your back. I have written bugs like this in C++, whose const implementation is very like those proposed for Java.

The only solution I know of is for the objects themselves to be immutable, not just the references to them. Java language support for this, via lightweight types or whatever, would be very welcome, and would give the JVM the opportunity for some spectacular optimizations.

m_r_atkinson
Offline
Joined: 2003-08-29

I agree, although I think that there is a case for having a "function" keyword to mark methods that have no side effects. This is they do not alter this or any of their parameters and only call other "function" methods (or any methods on immutable objects).

The main advantage is for the user of the function method, it is easy to see at a glance that such methods are safe from many subtle problems.

It should be possible for a JVM to (more easily) produce better optimisation for such methods. Potentially they may be run in parallel as by definition

x = fn1(a);
y = fn2(b);

is equal to

y = fn2(b);
x = fn1(a);

as long as x is distinct from b.

scott_sauyet
Offline
Joined: 2004-08-25

I'm not quite sure how this would work when classes are loaded dynamically. How could the compiler know what methods might change the state of an object? For instance, if we had this code:[code] private List list;
public void setList(List theList) {list = theList;}

public [u][i]fixed[/i][/u] List getList() {return list;}[/code]we cannot know that our putatively fixed reference to the list doesn't change the list. Even if the reference only called [code] list.get(0)[/code] our list instance might have been one of this class:[code]import java.util.ArrayList;

public class MyList extends ArrayList {

private int accesses;

public Object get(int index) throws IndexOutOfBoundsException {
accesses++;
return super.get(index);
}
}[/code]

I don't see any way around this. Obviously it could be a run-time solution, but that scares me.

jsbean
Offline
Joined: 2005-02-15

Agreed, I don't see a clean way around this either. Would have been nice though to get rid of this kind of method parameters for instance:

class Color {
...
public float[] getComponents(float[] compArray) {...}
...
}

compArray is passed into the method to be sent back filled with values from the method. The main reason why compArray is needed is so that Color can be sure that it's internal array of values won't be changed from outside.

public fixed float[] getComponents() {...} would have been nicer if it was possible

jwenting
Offline
Joined: 2003-12-02

final means the reference can't be changed. It's up to you to make your objects such that the content is guarded.

No handholding required.

jonbarril
Offline
Joined: 2004-12-15

And java used to mean "no assertions, no generics, no...". The nature of a language is to adapt to trends and changes (hopefully "good" ones), or die. I remember Gosling stating at an early conference that he would be surprised if Java was still around in 20 years, because concepts change, especially in this business.

Criticism of new suggestions is good, healthy, and appreciated. A bit more substance and a bit less rhetoric would also be greatly appreciated.

--jon

hlovatt
Offline
Joined: 2003-11-18

Check out:

http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4617197

This is a request for an enhancement for immutables.

Also:

https://pec.dev.java.net/nonav/compile/index.html

This is an extended compiler that does immutables amongst other things.

jsbean
Offline
Joined: 2005-02-15

Thank you for the pointers. What I want seems to be very similar to 'const' in C++.

There is some thought errors in my previous post, so let me clarify:

"fixed" would mean: "will not be changed by this reference or future references to this reference"

Ex.

class Node {
private Vector myChildren // not fixed
...
public fixed Vector getChildren() {
return myChildren;
}
...
}

...
fixed Vector children = node.getChildren();
Vector siblings = children; // compile error

In this code, children or any references to children cannot be changed, but myChildren can be changed.
So fixed is no garantee that an object won't be changed, only that a reference to it that is labeled fixed cannot change it.

Checking fixed references can be made entirely by the compiler and no changes to the byte-code is needed.

Once a reference is fixed it should stay fixed. making a fixed reference from a non-fixed one should not copy the object - hence there is no garantee that a fixed object won't change.

Difference from 'const': No copying of objects are necessary, no possibility to make a non-fixed reference from a fixed one.

Difference from Immutable objects: The same object can be both fixed and non-fixed at the same time - from different references. No possibility for compiler optimizations that are possible with immutable objects.

I think 'fixed' and 'Immutable' are two useful features that would complement each other. If the language supports both 'fixed' keyword and 'Immutable' interface, an immutable object would be fixed by default and a fixed reference would be final by default.

hlovatt
Offline
Joined: 2003-11-18

Personally I find immutable more useful than a const reference (fixed). When I need a const reference and not an immutable I simply use a const interface. EG

class ConstInteger {
protected int value;
ConstInteger( final int value ) { this.value = value; }
int getValue() { return value; }
}

class NoneConstInteger extends ConstInteger {
NoneConstInteger( final int value ) { this.value = value; }
void setValue( final int value ) { this.value = value; }
}

The rare occasions when I use the above is after profiling has shown that using immutables is too slow. This is normally for large objects (kilobyte sized arrays), I find for small objects (any thing less than 10 fields) just making a new immutable is fine, for medium objects (10 - 1,000 fields) I cache the immutable.

Using immutables simplifies your code and is thread safe. See Joshua Bloch, [i]Effective Java[/i], section on "Favor Immutability".