-
Enhancement
-
Resolution: Fixed
-
P4
-
1.1.3
-
1.2beta1
-
sparc
-
solaris_2.5.1, solaris_2.6
-
Not verified
Let's say we have a GUI class with many buttons. To handle the button
actions, we create one inner class per button. Let's also say we want
sub-classes of this GUI class to be able to override the button
actions. The easiest way is to create one protected method per action
listener inner class. Each inner class action listener just calls one
protected method.
This approach works fine, but it creates a huge number of inner
classes (and a lot of .class files). With the reflection package, I
can write one inner class which is given a Method object. When the
action listener is called, it calls the invoke() method of the Method
object.
This would be great, and it works fine if the GUI class and its
sub-class are in the same package. If the GUI class is in a package
and the sub-class is in a top-level application, for example, the code
fails with an .IllegalAccessException.
Here is some code which demonstrates the problem. There are two java
files: Main.java and MyFrame.java. Also included is a Makefile.
1) Create a directory.
2) Place Main.java and Makefile in this directory.
3) Create a sub-directory called COM in this directory.
4) Place MyFrame.java in this sub-directory.
5) Run 'make build'.
6) Run 'make run'.
7) When the Frame appears, select File / Test.
8) This should produce the output:
invoke-problem» make run
Received unexpected exception:
java.lang.IllegalAccessException: Main$MyOtherFrame
As an interesting exercise, make the following modifications:
1) Move MyFrame.java to the same directory as Main.java.
2) Edit MyFrame.java to remove the "package COM;" statement.
3) Change "COM/MyFrame" in Makefile to "MyFrame".
4) Run 'make build'.
5) Run 'make run'.
6) When the Frame appears, select File / Test.
7) The output is now:
invoke-problem» make run
Goodbye!
Hello!
This should be the output in BOTH cases. With this error, it
is difficult to take advantage of the reflections package from user
packages. The work-around in this case is to create a lot of inner
classes.
########## Makefile ###########
CLASSPATH = .
JAVAC = javac
JAVA = java
JAR = jar
RM = /usr/bin/rm -f
SRCS = COM/MyFrame.java Main.java
build:
$(JAVAC) -verbose $(SRCS)
run:
@JAVA_HOME=$(JAVA_HOME); export JAVA_HOME; CLASSPATH=$(CLASSPATH); export CLASSPATH; $(JAVA) Main
clean:
$(RM) *.class COM/*.class
######## Main.java #######
import COM.*;
import java.io.*;
import java.awt.*;
final class Main
{
Main()
{
MyFrame frame1 = createAppFrame();
}
//**********************************************************************
// Package Public
//**********************************************************************
public static void
main(
String[] args)
{
new Main();
}
MyFrame
createAppFrame()
{
MyOtherFrame frame = new MyOtherFrame();
frame.setSize(300,200);
frame.setVisible(true);
return frame;
}
//**********************************************************************
// Inner Classes
//**********************************************************************
//**********************************************************************
// Class MyOtherFrame
//**********************************************************************
class MyOtherFrame
extends MyFrame
{
MyOtherFrame()
{
super();
addFileMenu();
}
//----------------------------------------------------------------------
protected void
sayHello()
{
System.out.println("Goodbye!");
super.sayHello();
}
}
//**********************************************************************
// End Inner Classes
//**********************************************************************
}
######## COM/MyFrame.java ################
package COM;
import java.lang.reflect.Method;
import java.awt.*;
import java.awt.event.*;
public abstract class MyFrame
extends Frame
{
public
MyFrame()
{
menuBar = new MenuBar();
setMenuBar(menuBar);
// Add a window listener
addWindowListener(new MyFrameWindowListener());
}
protected void
sayHello()
{
System.out.println("Hello!");
}
protected void
addFileMenu()
{
Menu fileMenu = new Menu("File");
menuBar.add(fileMenu);
fileMenu.add(addMenuButton("sayHello"));
}
private MenuItem
addMenuButton(
String methodName)
{
MenuItem item = new MenuItem("Test");
try {
item.addActionListener(
new MenuButtonListener(getClass().
getDeclaredMethod(methodName, null)));
}
catch (Exception exception) {
System.out.println("Received unexpected exception:");
System.out.println(exception);
}
return item;
}
private MenuBar menuBar;
//**********************************************************************
// Inner Classes
//**********************************************************************
//**********************************************************************
// MenuButtonListener
//**********************************************************************
private final class MenuButtonListener
implements ActionListener
{
MenuButtonListener(
Method method)
{
this.method = method;
}
public void
actionPerformed(
ActionEvent e)
{
try {
method.invoke(MyFrame.this, null);
}
catch (Exception exception) {
System.out.println("Received unexpected exception:");
System.out.println(exception);
}
}
private Method method;
}
//**********************************************************************
// MyFrameWindowListener
//**********************************************************************
// The window was opened or closed.
private final class MyFrameWindowListener
extends WindowAdapter
{
public void
windowClosing(
WindowEvent e)
{
e.getWindow().setVisible(false);
}
}
//**********************************************************************
// End Inner Classes
//**********************************************************************
}
################ dir structure ###########
find . -name '*' -print
./Makefile
./COM
./COM/MyFrame.java
./Main.java
################ build ##############
quebec% make build
javac COM/MyFrame.java Main.java
################ running the application #########
quebec% make run
(A java application windows pops up with one 'File'
menu. Mouse pointing to File->test causes the following error:
"Received unexpected exception:
java.lang.IllegalAccessException: Main$MyOtherFrame"
actions, we create one inner class per button. Let's also say we want
sub-classes of this GUI class to be able to override the button
actions. The easiest way is to create one protected method per action
listener inner class. Each inner class action listener just calls one
protected method.
This approach works fine, but it creates a huge number of inner
classes (and a lot of .class files). With the reflection package, I
can write one inner class which is given a Method object. When the
action listener is called, it calls the invoke() method of the Method
object.
This would be great, and it works fine if the GUI class and its
sub-class are in the same package. If the GUI class is in a package
and the sub-class is in a top-level application, for example, the code
fails with an .IllegalAccessException.
Here is some code which demonstrates the problem. There are two java
files: Main.java and MyFrame.java. Also included is a Makefile.
1) Create a directory.
2) Place Main.java and Makefile in this directory.
3) Create a sub-directory called COM in this directory.
4) Place MyFrame.java in this sub-directory.
5) Run 'make build'.
6) Run 'make run'.
7) When the Frame appears, select File / Test.
8) This should produce the output:
invoke-problem» make run
Received unexpected exception:
java.lang.IllegalAccessException: Main$MyOtherFrame
As an interesting exercise, make the following modifications:
1) Move MyFrame.java to the same directory as Main.java.
2) Edit MyFrame.java to remove the "package COM;" statement.
3) Change "COM/MyFrame" in Makefile to "MyFrame".
4) Run 'make build'.
5) Run 'make run'.
6) When the Frame appears, select File / Test.
7) The output is now:
invoke-problem» make run
Goodbye!
Hello!
This should be the output in BOTH cases. With this error, it
is difficult to take advantage of the reflections package from user
packages. The work-around in this case is to create a lot of inner
classes.
########## Makefile ###########
CLASSPATH = .
JAVAC = javac
JAVA = java
JAR = jar
RM = /usr/bin/rm -f
SRCS = COM/MyFrame.java Main.java
build:
$(JAVAC) -verbose $(SRCS)
run:
@JAVA_HOME=$(JAVA_HOME); export JAVA_HOME; CLASSPATH=$(CLASSPATH); export CLASSPATH; $(JAVA) Main
clean:
$(RM) *.class COM/*.class
######## Main.java #######
import COM.*;
import java.io.*;
import java.awt.*;
final class Main
{
Main()
{
MyFrame frame1 = createAppFrame();
}
//**********************************************************************
// Package Public
//**********************************************************************
public static void
main(
String[] args)
{
new Main();
}
MyFrame
createAppFrame()
{
MyOtherFrame frame = new MyOtherFrame();
frame.setSize(300,200);
frame.setVisible(true);
return frame;
}
//**********************************************************************
// Inner Classes
//**********************************************************************
//**********************************************************************
// Class MyOtherFrame
//**********************************************************************
class MyOtherFrame
extends MyFrame
{
MyOtherFrame()
{
super();
addFileMenu();
}
//----------------------------------------------------------------------
protected void
sayHello()
{
System.out.println("Goodbye!");
super.sayHello();
}
}
//**********************************************************************
// End Inner Classes
//**********************************************************************
}
######## COM/MyFrame.java ################
package COM;
import java.lang.reflect.Method;
import java.awt.*;
import java.awt.event.*;
public abstract class MyFrame
extends Frame
{
public
MyFrame()
{
menuBar = new MenuBar();
setMenuBar(menuBar);
// Add a window listener
addWindowListener(new MyFrameWindowListener());
}
protected void
sayHello()
{
System.out.println("Hello!");
}
protected void
addFileMenu()
{
Menu fileMenu = new Menu("File");
menuBar.add(fileMenu);
fileMenu.add(addMenuButton("sayHello"));
}
private MenuItem
addMenuButton(
String methodName)
{
MenuItem item = new MenuItem("Test");
try {
item.addActionListener(
new MenuButtonListener(getClass().
getDeclaredMethod(methodName, null)));
}
catch (Exception exception) {
System.out.println("Received unexpected exception:");
System.out.println(exception);
}
return item;
}
private MenuBar menuBar;
//**********************************************************************
// Inner Classes
//**********************************************************************
//**********************************************************************
// MenuButtonListener
//**********************************************************************
private final class MenuButtonListener
implements ActionListener
{
MenuButtonListener(
Method method)
{
this.method = method;
}
public void
actionPerformed(
ActionEvent e)
{
try {
method.invoke(MyFrame.this, null);
}
catch (Exception exception) {
System.out.println("Received unexpected exception:");
System.out.println(exception);
}
}
private Method method;
}
//**********************************************************************
// MyFrameWindowListener
//**********************************************************************
// The window was opened or closed.
private final class MyFrameWindowListener
extends WindowAdapter
{
public void
windowClosing(
WindowEvent e)
{
e.getWindow().setVisible(false);
}
}
//**********************************************************************
// End Inner Classes
//**********************************************************************
}
################ dir structure ###########
find . -name '*' -print
./Makefile
./COM
./COM/MyFrame.java
./Main.java
################ build ##############
quebec% make build
javac COM/MyFrame.java Main.java
################ running the application #########
quebec% make run
(A java application windows pops up with one 'File'
menu. Mouse pointing to File->test causes the following error:
"Received unexpected exception:
java.lang.IllegalAccessException: Main$MyOtherFrame"
- relates to
-
JDK-4157089 Fields that could use AccessibleObject.setAccessible() cannot be created.
-
- Closed
-