Algorithms and OOD (CSC 207 2014S) : Readings
Primary: [Front Door] [Schedule] - [Academic Honesty] [Disabilities] [Email] - [FAQ] [Teaching & Learning] [Grading] [Rubric] - [Calendar]
Current: [Assignment] [EBoard] [Lab] [Outline] [Reading]
Sections: [Assignments] [EBoards] [Examples] [Handouts] [Labs] [Outlines] [Partners] [Readings]
Reference: [Java 7 API] [Java Code Conventions] [GNU Code Conventions]
Related Courses: [CSC 152 2006S (Rebelsky)] [CSC 207 2013F (Rebelsky)] [CSC 207 2013S (Walker)] [CSC 207 2011S (Weinman)]
Misc: [SamR] [Glimmer Labs] [CS@Grinnell] [Grinnell] [Issue Tracker (Course)] [Issue Tracker (Textbook)]
Summary: We consider Java's “anonymous inner classes” as a programming technique.
As you may have noted, in much of our recent work, we've ended up creating classes that (a) are used by only one other class or (b) are effectively used only once. For example,
For those used to functional programming, the first two issues
suggest that we might use anonymous functions.
For example, in Scheme, to select all the values less than 10 in a list
of numbers, we might write (list-select numbers (lambda (val)
(< val 10)))
or (list-select numbers (r-s < 10))
.
Java (at least Java up through version 7) does not include functions as a first-class data type, so it's unlikely that we'd have anonymous functions in Java. However, in version 1.1, Java introduced anonymous classes, which can help serve the same purpose. In this reading, we explore the usage of anonymous inner classes.
While we will eventually explore anonymous inner classes in the context of predicates, comparators, iterators, and more, let's start with an invented situation that is simultaneously simpler and more complex than some of these examples.
At times, we want objects that can greet the user. We'll call such objects Greeters, and give them a simple interface.
package taojava.aic; import java.io.PrintWriter; /** * A very simple interface to be used in illustrating anonymous inner classes. */ public interface Greeter { /** * Print a greeting. */ public void greet(PrintWriter pen); } // interface Greeter
Here's a very simple object that implements the Greeter interface.
package taojava.aic; import java.io.PrintWriter; /** * A very simple example of a greeter. */ public class SampleGreeter implements Greeter { public void greet(PrintWriter pen) { pen.println("Hello world."); } // greet } // class SampleGreeter
Now, let's think like Java designers. Sometimes, given an object, we might want to make a greeter for that object. Why doesn't the object want to be it's own greeter? Maybe we need more than one, and want each to behave differently, or at least independently. (In case you couldn't tell, we're reflecting on iterators and iterables, as well as some other paired interfaces.) We'll call things that generate greeters Greetables.
package taojava.aic; import java.io.PrintWriter; /** * A very simple interface to be used in illustrating anonymous inner classes. */ public interface Greetable { /** * Get the greeter. */ public Greeter greeter(); } // interface Greetable
And here's a really simple implementation.
package taojava.aic; import java.io.PrintWriter; /** * A very simple class that creates a Greeter. */ public class SampleGreetable1 implements Greetable { @Override public Greeter greeter() { return new SampleGreeter(); } // greeter } // interface SampleGreetable1
You haven't seen anything too surprising so far, or at least I hope you
haven't. Now, let's move a step further. Suppose we want to create
a new Greetable that returns a greeter that returns the string
“Hi!”. We could use the same technique as above. But
we're creating a Greeter
class that only gets used
once. What's the alternative?
The alternative is an anonymous inner class. Rather than writing down
the full class definition, we can use the keyword new
,
the interface name, and then the body of a class. That is,
package taojava.aic; import java.io.PrintWriter; /** * A simple class that builds an anonymous greeter. */ public class SampleGreetable2 implements Greetable { @Override public Greeter greeter() { return new Greeter() { @Override public void greet(PrintWriter pen) { pen.println("Hi"); } // greet(PrintWriter) }; // new Greeter } // greeter() } // interface SampleGreetable2
Are there any advantages to this approach? After all, it's not that
much less code than in the previous example. The most obvious benefit
is that we've achieved fairly strong encapsulation -
since the class is within SampleGreetable2
, nothing else
can access it. We also have the normal benefit of anonymity - if the
class is only used here, there's no need to come up with a name for
it. But these two reasons alone are not the only reasons we like
anonymous inner classes.
Once of the nice things about inner classes is that they can reference the fields of the enclosing class. In the following example, when we construct the Greeter, we copy and change a field in the enclosing class.
package taojava.aic; import java.io.PrintWriter; /** * A simple class that creates an anonymous greeter and references an internal * field of the enclosing class during creation. */ public class SampleGreetable3 implements Greetable { int i = 0; @Override public Greeter greeter() { return new Greeter() { int num = ++i; @Override public void greet(PrintWriter pen) { pen.println("Number " + num); } // greet(PrintWriter) }; // new Greeter } // greeter() } // interface SampleGreetable3
Suppose we wrote the following.
PrintWriter pen = new PrintWriter(System.out, true); Greetable g = new SampleGreetable3(); Greeter[] greeters = new Greeter[] { g.greeter(), g.greeter(), g.greeter(), g.greeter(), g.greeter() }; for (int i = 0; i < greeters.length; i++) { pen.print(i + ": "); greeters[i].greet(pen); } // for
Our output will be
0: Number 1 1: Number 2 2: Number 3 3: Number 4 4: Number 5
Of course, we could achieve the same effect by writing a separate
class and giving it a constructor that takes i
as a
parameter, but that's a lot more coding.
But we're not done yet! Not only can we reference the fields of an enclosing class when we build the inner class, we can even reference them when we run the method.
package taojava.aic; import java.io.PrintWriter; /** * A simple class that creates an anonymous greeter that references a field in * the enclosing class at the time is built and at the time it greets. */ public class SampleGreetable4 implements Greetable { int i = 0; @Override public Greeter greeter() { return new Greeter() { int num = ++i; @Override public void greet(PrintWriter pen) { pen.println(num + " of " + i); } // greet(PrintWriter) }; // new Greeter } // greeter() } // interface SampleGreetable4
If we run code similar to the above, we'll get
0: 1 of 5 1: 2 of 5 2: 3 of 5 3: 4 of 5 4: 5 of 5
Things are going pretty well. We can reference a field of the enclosing class when we build an object in an inner anonymous class. We can reference a field of the enclosing class when someone invokes the object's methods. What else would we want to do? Well, if we are used to a functional model, we might even want to be able to reference a parameter or local variable in an enclosing method. For example, we might want to write something like the following:
Greeter makeGreeter(int n) { return new Greeter() { @Override public void greet(PrintWriter pen) { pen.println(n); } // greet(PrintWriter) }; // new Greeter } // makeGreeter
However, if we try that, the Java compiler will greet us with a friendly message something like the following:
SampleGreetable5.java:19: error: local variable n is accessed from within inner class; needs to be declared final pen.println(n); ^ 1 error
What's going on here? The Java compiler is worried that n
gets referenced in a method that may be called when that method is no
longer in scope. (And we can be pretty sure that it won't be in scope
when greet
gets called.) Some languages like Scheme have
a clever way of dealing with these issues. Java, on the other hand,
doesn't want to have to deal with it. So, Java is only willing to let
you reference the parameter if it knows that the parameter won't change,
in which case it can just grab the current value. The final
modifier is how you tell Java that it won't change. With this update,
we can write the following:
package taojava.aic; import java.io.PrintWriter; /** * A simple class with an extra method that creates an anonymous Greeter that * references a parameter. */ public class SampleGreetable5 implements Greetable { int i = 0; @Override public Greeter greeter() { return makeGreeter(++i); } // greeter() Greeter makeGreeter(final int n) { return new Greeter() { @Override public void greet(PrintWriter pen) { pen.println(n); } // greet(PrintWriter) }; // new Greeter } // makeGreeter } // interface SampleGreetable5
this
” Mean?
You'll note that we have not been using this
in referencing
fields. That's because this
can feel a bit ambiguous
for inner classes - does this
refer to the inner class
that we're building or to the enclosing class? Let's try updating
the 4th example to see what the Java compiler tells us.
public void greet(PrintWriter pen) { pen.println(this.num + " of " + this.i); } // greet(PrintWriter)
As you might expect, we get another helpful message.
SampleGreetable6.java:17: error: cannot find symbol pen.println(this.num + " of " + this.i); ^ symbol: variable i 1 error
We've learned a lot from that message. The compiler didn't complain about
this.num
, so it seems that this
refers to the
inner class. It did, however, complain about this.i
, and
so this
does not refer to the enclosing class. It turns
out that to refer to the enclosing class, you prefix this
with the class name.
package taojava.aic; import java.io.PrintWriter; /** * Explicit references to the location of various fields. */ public class SampleGreetable6 implements Greetable { int i = 0; @Override public Greeter greeter() { return new Greeter() { int num = ++SampleGreetable6.this.i; @Override public void greet(PrintWriter pen) { pen.println(this.num + " of " + SampleGreetable6.this.i); } // greet(PrintWriter) }; // new Greeter } // greeter() } // interface SampleGreetable6
At this point you may be saying to yourself “Cool! I can use this when working with predicates.” Or you may be asking yourself “How can I use this in practice?” (Guess what, one answer is “with predicates”.) So let's do a quick exploration of why anonymous inner classes might be useful for predicates.
We'll start with a sample use of predicates, printing all of the values in an array for which the predicate holds.
/** * Print all the elements of the array for which pred holds. */ public static <T> void printMatching(PrintWriter pen, T[] vals, Predicate<T> pred) { for (T val: vals) { if (pred.test(val)) { pen.println(val + " "); } // if } // for pen.println(); } // printMatching(PrintWriter, T[], Predicate<T>)
Now, suppose we have an array of strings and want all of the short strings (say the strings of length less than or equal to 4). We could write something like the following:
Predicate<String> small = new Predicate<String>() { @Override public boolean test(String str) { return str.length() <= 4; } // test(String) }; // new Predicate<String>
Now we can print the small strings in our test with something like
printMatching(pen, essay, small);
Students who have not yet learned about iterators can ignore this section. Sorry.
You may recall that way back at the start of this reading, we considered problems in which we create one-off classes, classes that only need to be used only in one situation. We've looked at Predicates. It's likely that comparators will be similar. But how is this all useful for iterators? Well, most of the times we've written iterators, we've need to refer to fields and of the enclosing class. And so it's much easier to write iterators as anonymous inner classes, rather than as helper classes. Here's a fraction of a typical stack class.
public class Stack<T> implements Iterable { T[] values; int size; // ... public Iterator<T> iterator() { return new Iterator<T>() { int i = size-1; @Override public T next() throws NoSuchElementException { if (hasNext()) { throw new NoSuchElementException(); } // if (hasNext()) return Stack.this.values[this.i--]; } // next() @Override public boolean hasNext() { return this.i >= 0; } // hasNext() @Override public void remove() { throw new UnsupportedOperationException(); } // remove() }; // new Iterator<T> } // iterator() } // class Stack<T>
Like anonymous functions and named lets in Scheme, anonymous inner classes are powerful and convenient for experienced programmers and a bit overwhelming for novice programmers. Take it slowly, and you'll soon find that anonymous inner classes will be a useful tool for many situations.
Primary: [Front Door] [Schedule] - [Academic Honesty] [Disabilities] [Email] - [FAQ] [Teaching & Learning] [Grading] [Rubric] - [Calendar]
Current: [Assignment] [EBoard] [Lab] [Outline] [Reading]
Sections: [Assignments] [EBoards] [Examples] [Handouts] [Labs] [Outlines] [Partners] [Readings]
Reference: [Java 7 API] [Java Code Conventions] [GNU Code Conventions]
Related Courses: [CSC 152 2006S (Rebelsky)] [CSC 207 2013F (Rebelsky)] [CSC 207 2013S (Walker)] [CSC 207 2011S (Weinman)]
Misc: [SamR] [Glimmer Labs] [CS@Grinnell] [Grinnell] [Issue Tracker (Course)] [Issue Tracker (Textbook)]
Copyright (c) 2013-14 Samuel A. Rebelsky.
This work is licensed under a Creative Commons Attribution 3.0 Unported License. To view a copy of this
license, visit http://creativecommons.org/licenses/by/3.0/
or send a letter to Creative Commons, 543 Howard Street, 5th Floor,
San Francisco, California, 94105, USA.