As far as I can tell, the role of generator is essentially that of an
Iterator, it provides a mechanism for doing something to each element in a
"collection" (not necessarily a Collection). The major differences being:
* Generator has a "close" method (currently stop()). This is used for
things that need to clean up after themselves a little bit, like closing
files or sockets. EachLine was an example of this.
* Generator has convenience methods for "internal iteration"
(Algorithms.*)
* Generator doesn't have a next() function, currently it only exposes the
"internal iteration" methods like "run(UnaryProcedure)"
It seems to me that it is possible to simplify and unify these two
concepts with an implementation like the following:
interface Generator extends Iterator {
/** "stops" this Generator, freeing any associated resources */
void stop();
// the "convenience methods", if desired at this level
Generator apply(UnaryFunction f);
boolean contains(UnaryPredicate p);
Object detect(UnaryPredicate p);
// etc.
}
abstract class BaseGenerator implements Generator {
public abstract Object next();
public void remove() {
throw new UnsupportedOperationException();
}
public boolean hasNext() {
return !(isStopped());
}
public void stop() {
closed = true;
}
// insert implementations of the "convenience methods" here, e.g.,
public void foreach(UnaryProcedure proc) {
while(hasNext()) {
proc.execute(next());
}
}
/** note this method is protected here */
protected boolean isStopped() {
return closed;
}
protected void finalize() {
if(!isStopped()) { stop(); }
}
private boolean closed = false;
}
Implementations of Generator would then look something like:
/**
* This one is infinite, if you don't call
* stop(), it'll generate for ever.
*/
class RandomIntegers extends BaseGenerator {
public Object next() {
return new Integer(random.nextInt());
}
private Random random = new Random();
}
or,
/**
* This is finite and doesn't really need
* to be manually stopped. It's really no
* better than an Iterator, except it adds
* the convenience methods like:
* Elements.from(myArray).contains(myPredicate);
*/
class Elements extends BaseGenerator {
public Elements(Object[] values) {
this.values = values;
this.next= 0;
}
public boolean hasNext() {
return !isStopped() && (next < values.length);
}
public Object next() {
return values[next++];
}
public static Elements from(Object[] values) {
return new Elements(values);
}
/** You could override stop() here if you want: */
public void stop() {
values = null;
super.stop();
}
private int next;
private Object[] values;
}
or,
/**
* This one actually needs to be stopped,
* although the finalizer will protect
* you a little bit. (And we call
* stop() internally if you iterate all
* the way to the end of the stream.)
*
* You can treat this as an Iterator
* (i.e., pass it off to a method that
* expects an Iterator), but you'll
* have to follow the "close what you open"
* strategy and call stop() yourself.
*/
class Lines extends BaseGenerator {
public Lines(BufferedReader in) {
this.in = in;
}
public boolean hasNext() {
return !isStopped() && (nextSet || setNext());
}
public Object next() {
if(hasNext()) {
nextSet = false;
return next;
} else {
throw new NoSucheElementException();
}
}
public void stop() {
in.close();
in = null;
next = null;
super.stop();
}
private boolean setNext() {
next = in.readLine();
if(null == next) {
stop();
nextSet = false;
} else {
nextSet = true;
}
return nextSet;
}
private boolean nextSet = false;
private String next = null;
private BufferedReader in;
}
etc.
Is there a disadvantage to doing it this way that I'm missing?
- Rod <http://radio.weblogs.com/0122027/>
---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]