Anda di halaman 1dari 11

CORE JAVA TECHNOLOGIES TECH TIPS

TIPS, TECHNIQUES, AND SAMPLE CODE


Welcome to the Core Java(tm) Technologies Tech Tips, October 8,
2002. Here you'll get tips on using core Java technologies and
APIs, such as those in Java 2 Platform, Standard Edition
(J2SE(tm)).
This issue covers:
* Using Regular Expression Groups
* Anonymous Classes
These tips were developed using Java 2 SDK, Standard Edition,
v 1.4.
You can view this issue of the Tech Tips on the Web at
http://java.sun.com/jdc/JDCTechTips/2002/tt1008.html
See the Subscribe/Unsubscribe note at the end of this newsletter
to subscribe to Tech Tips that focus on technologies and products
in other Java platforms.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - USING REGULAR EXPRESSION GROUPS
Regular expressions are a new feature of the Java 2 Platform,
Standard Edition v 1.4. A regular expression is a string pattern
that you can use to perform sophisticated string searching and
replacement. For example, the regular expression:
\d+
means "a sequence of one or more consecutive digit characters".
A regular expression is represented by (compiled into) an
instance of the java.util.regex.Pattern class. Then the related
Matcher class is used to match input character sequences against
the pattern.
Sometimes when matching is done, all you care about is whether
the pattern was found somewhere in the input sequence. A common
application of this is a utility that searches a text file and
prints lines that match a specified pattern.
But sometimes you might want to get more information about a
match than simply an indication of whether the pattern was found
in the input. For example, if you're searching for a number,
using the regular expression illustrated above, you might want to
retrieve the actual text of the number that was found.
This is one situation where regular expression groups are useful.
A group is a numbered part of a regular expression. For example,
in the following expression:
(\d+)zzz
there are two groups. Group 0 always refers to the whole

expression, and group 1 to the subexpression that starts with the


open parenthesis "(" and ends with the corresponding close
parenthesis ")". The text of matched groups is saved by the
regular expression matcher, and can be retrieved or referenced
later in the regular expression.
Let's illustrate these ideas with an example:
import java.util.regex.*;
public class GroupDemo1 {
static final String stringlist[] = {
"abc 123 def",
"456 ghi",
"jkl789mno"
};
public static void main(String args[]) {
// compile regular expression pattern for a
// number consisting of one or more digits
Pattern patt = Pattern.compile("(\\d+)");
for (
int i = 0; i < stringlist.length; i++) {
String currstr = stringlist[i];
// see if the current string has a match
Matcher match = patt.matcher(currstr);
// if a match, print the string text
// for the matching group (group 1)
if (match.find()) {
System.out.println("For \"" +
currstr + "\" match is: " +
match.group(1));
}
}
}
}
In this demonstration, GroupDemo1, there are some strings with
embedded numbers in them. The program uses regular expression
features to search for the numbers, and display the string text
for each number that it finds. The program creates a Matcher
object for each input string. Then the program calls the find
method to see if there is a match against the regular expression
pattern. If there is a match, the program gets the matching text
for group 1 from the Matcher and displays it. In this example,
the regular expression is:
(\d+)
and group 1 is the text matched by "\d+". When you run the
program, you should see the result:
For "abc 123 def" match is: 123

For "456 ghi" match is: 456


For "jkl789mno" match is: 789
Let's look at another example similar to the first one:
import java.util.regex.*;
public class GroupDemo2 {
static final String stringlist[] = {
"abc 123 def 123",
"456 ghi",
"jkl789mno
789pqr",
"123 456"
};
public static void main(String args[]) {
//
//
//
//

compile regular expression pattern for


a number consisting of one or more
digits followed by the same number later
in the string

Pattern patt =
Pattern.compile("(\\d+).*\\1");
for (
int i = 0; i < stringlist.length; i++) {
String currstr = stringlist[i];
// see if the current string
// has a match
Matcher match = patt.matcher(currstr);
// if a match, print the string text
// for the matching group (group 1)
if (match.find()) {
System.out.println("For \"" +
currstr + "\" match is: " +
match.group(1));
}
}
}
}
The GroupDemo2 demo is almost the same as GroupDemo1, but with
a different regular expression:
(\d+).*\1
For an input sequence to match this expression, it must contain a
number followed by any characters followed by the original number.
The "\1" is a back reference to the first group in the expression.
So input of:
123 abc 123
will match, but:

123 abc 456


will not.
When you run the GroupDemo2 program, you should see the result:
For "abc 123 def 123" match is: 123
For "jkl789mno
789pqr" match is: 789
Another way that you can use groups is to do string editing, for
example, replacing the text of a matching group with other text.
Here's an example:
import java.util.regex.*;
public class GroupDemo3 {
static final String stringlist[] = {
"abc 123 def 123",
"456 ghi",
"no match",
"jkl789mno 789",
"",
"123.123",
"1,2,3,4,5,6,7,8,9,10"
};
public static void main(String args[]) {
// compile regular expression pattern for
// a number consisting of one or more
// digits
Pattern patt = Pattern.compile("(\\d+)");
for (
int i = 0; i < stringlist.length; i++) {
String currstr = stringlist[i];
String outstr;
// see if the current string has
// a match
Matcher match = patt.matcher(currstr);
boolean result = match.find();
// if found a match, then go through
// string and replace all matches with
// "[matchstring]"
if (result) {
StringBuffer strbuf =
new StringBuffer();
do {
match.appendReplacement(
strbuf, "[$1]");
result = match.find();
} while (result);
match.appendTail(strbuf);
outstr = strbuf.toString();

}
// if no match, just point output
// at input
else {
outstr = currstr;
}
// display the result
System.out.println(outstr);
}
}
}
The GroupDemo3 program finds numbers in the input, as before.
When a number is found, it takes the text of the number and puts
"[]" around it. Ultimately, the program produces an output string
that consists of the input with this editing operation performed
on it.
The appendReplacement and appendTail methods are the key to
performing this operation. The appendReplacement method is used
to replace matched text in the input with a new string that you
specify. The string can contain references to groups. For example,
the GroupDemo3 program specifies the replacement string as "[$1]".
This means to replace matched instances of group 1 with the
group 1 text surrounded by "[]", and append the result to an
output string buffer. When the matching process is exhausted,
appendTail is used to append the rest of the input to the output.
The output of the GroupDemo3 program is:
abc [123] def [123]
[456] ghi
no match
jkl[789]mno [789]
[123].[123]
[1],[2],[3],[4],[5],[6],[7],[8],[9],[10]
The logic in this demo is very similar to the Matcher.replaceAll
method.
For further information about regular expressions, see
the article "Regular Expressions and the Java Programming
Language" by Dana Nourie and Mike McCloskey
(http://java.sun.com/jdc/technicalArticles/releases/1.4regex/).
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ANONYMOUS CLASSES
If you've done much Java programming, you might have realized
that it's possible to declare classes that are nested within
other classes. This tip is going to look at one particular kind
of nesting, which goes by the name "anonymous class". To get
started on the discussion, let's look at a simple example:
class Base {

void method1() {}
void method2() {}
}
class A { // normal class
static class B {} // static nested class
class C {} // inner class
void f() {
class D {} // local inner class
}
void g() {
// anonymous class
Base bref = new Base() {
void method1() {}
};
}
}
The example illustrates various kinds of nested and inner
classes. A nested class that is not declared static is called an
inner class. In the example code, B is a nested class, while C is
a nested class and an inner class.
The main focus of this tip is anonymous classes. You can glean
a few insights about anonymous classes by studying the example
above. One key idea is that an anonymous class has no name. An
anonymous class is a subclass of an existing class (Base in this
example) or an implementation of an interface.
Because an anonymous class has no name, it cannot have an
explicit constructor. Neither can an anonymous class be referred
to outside its declaring expression, except indirectly through
a superclass or interface object reference. Anonymous classes
are never static, never abstract, and always final. Also, each
declaration of an anonymous class is unique. For example, the
following code declares two distinct anonymous classes:
Base bref1 = new Base() {
void method1() {}
};
Base bref2 = new Base() {
void method1() {}
};
Each anonymous class is declared within an expression.
Let's look at a common situation where you would use an anonymous
class:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class AnonDemo2 {
public static void main(String args[]) {

// create a JFrame and add a listener


// to it to handle window closing
JFrame frame = new JFrame("AnonDemo2");
frame.addWindowListener(
new WindowAdapter() {
public void windowClosing(
WindowEvent e) {
System.exit(0);
}
});
// create a JPanel and add it to the frame
JPanel panel = new JPanel();
panel.setPreferredSize(
new Dimension(300, 300));
frame.getContentPane().add(panel);
// display the frame
frame.pack();
frame.setVisible(true);
}
}
This example displays a JPanel on the screen. The demo adds a
window listener to the JFrame object such that when the user
closes the window, the application terminates.
WindowListener is an interface. An implementing class must define
all the methods specified in the interface. WindowAdapter
implements the interface using dummy methods, like this:
public abstract class WindowAdapter
implements WindowListener, WindowStateListener,
WindowFocusListener
{
public void windowOpened(WindowEvent e) {}
public void windowClosing(WindowEvent e) {}
public void windowClosed(WindowEvent e) {}
public void windowIconified(WindowEvent e) {}
public void windowDeiconified(WindowEvent e) {}
public void windowActivated(WindowEvent e) {}
public void windowDeactivated(WindowEvent e) {}
public void windowStateChanged(WindowEvent e) {}
public void windowGainedFocus(WindowEvent e) {}
public void windowLostFocus(WindowEvent e) {}
}
The demo application, AnonDemo2, needs to override just one of
these methods, the windowClosing one. So it subclasses the
adapter class and overrides the single method. The subclass is
used only once within the application, and contains very simple
logic. That's why an anonymous class is a good choice in this
situation. The anonymous class extends the WindowAdapter class to
override a single method. WindowAdapter, in turn, implements the
WindowListener class by use of dummy methods that do nothing.

Let's look at another example. Suppose that you have a List of


Integer objects, and you want to sort the list, both in ascending
order (the default) and in descending order. Here's some code to
do this:
import java.util.*;
public class AnonDemo3 {
public static void main(String args[]) {
// create an ArrayList and add
// some Integer objects to it
List list = new ArrayList();
list.add(new Integer(37));
list.add(new Integer(-59));
list.add(new Integer(83));
// sort the list in the usual (ascending order)
Collections.sort(list);
System.out.println(list);
// sort the list in descending order
// using a function object implemented
// via an anonymous class
Collections.sort(list, new Comparator() {
public int compare(Object o1, Object o2) {
int a = ((Integer)o1).intValue();
int b = ((Integer)o2).intValue();
return a < b ? 1 : a == b ? 0 : -1;
}
});
System.out.println(list);
}
}
The program does the first sort in the obvious fashion. Then, to
sort in descending order, the program must specify a Comparator
function object. This object implements comparison logic to sort
Integer objects in descending order.
This demo uses an anonymous class, one that implements the
java.util.Comparator interface. If this kind of sorting is done
in only one place in an application, an anonymous class makes
sense, but if it's done many places, it might make more sense to
introduce a top-level or static nested class:
class MyComparator implements Comparator {
...
}
and implement the sort logic only once.
The output of the program is:
[-59, 37, 83]
[83, 37, -59]

Let's look at a final example, one that illustrates a couple of


issues related to the use of anonymous classes:
class A {
int afield;
// set value of afield
A(int afield) {
this.afield = afield;
}
// get value of afield
int getValue() {
return afield;
}
}
public class AnonDemo4 {
static A createAnon() {
final int dlocal = 40;
// return from the f() method an instance
// of the anonymous class derived from A
// invoke superclass constructor
return new A(10) {
int bfield = 20;
int cfield;
{
cfield = 30;
}
int getValue() {
return afield + bfield + cfield
+ dlocal;
}
};
}
public static void main(String args[]) {
A anonref = createAnon();
System.out.println(anonref.getValue());
}
}
In this example, the createAnon method declares an anonymous
class and returns a superclass (A) reference to an instance of
the anonymous class. This means that the anonymous class instance
can be used outside the declaring context (createAnon). Then the
getValue method is called on the anonymous class object reference.
Recall that anonymous classes do not have names, and so, they
cannot have explicit constructors. But there are several ways to
get around this limitation. When an instance of an anonymous
class is created, by saying:

new A(10) {...}


the superclass constructor, A(int), is automatically called.
Instance initialization for the anonymous class instance is
handled in the normal way, so that
int bfield = 20;
and
{
cfield = 30;
}
work as usual. These mechanisms can be used to do part of the
work that might normally be done in a constructor.
There's one additional unusual feature of the AnonDemo4 example.
The dlocal variable is declared as final. If the final keyword is
left off the declaration, this code will evoke a compiler error.
Why? Because it's possible, as this example illustrates, to refer
to an anonymous class object outside the context where the class
was declared. If such a reference is made, what value would
dlocal have, given that it's a local variable declared in the
createAnon method? This is a classic programming issue that occurs
when an invalid stack frame is referenced.
To get around this problem, the local variable must be made final,
that is, bound to a particular value which can be used in place
of the variable (dlocal) itself. So instead of using "dlocal",
the value "40" is used.
Anonymous classes are very useful tools, but they should not be
overemphasized. Some other kind of class might be a better choice
if more than one anonymous class in your application uses the
same logic, or if the logic in such a class is complicated, or if
you have deep class nesting. Also, anonymous class declarations
can be hard to read, so you should keep them simple.
For further information about anonymous classes, see section 5.4
Anonymous Inner Classes, in "The Java(tm)
Programming Language Third Edition" by Arnold, Gosling, and Holmes
(http://java.sun.com/docs/books/javaprog/thirdedition/). Also see
item 18 "Favor static member classes over nonstatic" in
"Effective Java Programming Language Guide" by Joshua Bloch
(http://java.sun.com/docs/books/effective/).
. . . . . . . . . . . . . . . . . . . . . . .
IMPORTANT: Please read our Terms of Use, Privacy, and Licensing
policies:
http://www.sun.com/share/text/termsofuse.html
http://www.sun.com/privacy/
http://developer.java.sun.com/berkeley_license.html
* FEEDBACK
Comments? Send your feedback on the Core Java Technologies Tech
Tips to:
jdc-webmaster@sun.com

* SUBSCRIBE/UNSUBSCRIBE
Subscribe to other Java developer Tech Tips:
- Enterprise Java Technologies Tech Tips. Get tips on using
enterprise Java technologies and APIs, such as those in the
Java 2 Platform, Enterprise Edition (J2EE(tm)).
- Wireless Developer Tech Tips. Get tips on using wireless
Java technologies and APIs, such as those in the Java 2
Platform, Micro Edition (J2ME(tm)).
To subscribe to these and other JDC publications:
- Go to the JDC Newsletters and Publications page,
(http://developer.java.sun.com/subscription/),
choose the newsletters you want to subscribe to and click
"Update".
- To unsubscribe, go to the subscriptions page,
(http://developer.java.sun.com/subscription/),
uncheck the appropriate checkbox, and click "Update".
- To use our one-click unsubscribe facility, see the link at
the end of this email:
- ARCHIVES
You'll find the Core Java Technologies Tech Tips archives at:
http://java.sun.com/jdc/TechTips/index.html
- COPYRIGHT
Copyright 2002 Sun Microsystems, Inc. All rights reserved.
901 San Antonio Road, Palo Alto, California 94303 USA.
This document is protected by copyright. For more information, see:
http://java.sun.com/jdc/copyright.html
This issue of the JDC Tech Tips is written by Glen McCluskey.
Core Java Technologies Tech Tips
October 8, 2002
Sun, Sun Microsystems, Java, Java Developer Connection, J2SE,
J2EE, and J2ME are trademarks or registered trademarks of
Sun Microsystems, Inc. in the United States and other countries.