B.4. Scripted Methods and Objects

You can declare and use methods in BeanShell, just as you would inside a Java class:

 int addTwoNumbers( int a, int b ) {
 return a + b;
 }
 sum = addTwoNumbers( 5, 7 ); // 12


BeanShell methods may also have dynamic (loose) argument and return types.

 add( a, b ) {
 return a + b;
 }
 foo = add(1, 2); // 3
 foo = add("Hello ", "Kitty"); // "Hello Kitty"


In BeanShell, as in JavaScript and Perl, method closures can take the place of classes for scripting objects (but in BeanShell you can also use the regular class syntax). You can turn the context of a method call into an object reference by having the method return the special value this. You can then use the this reference to refer to any variables that were set during the method call. To be useful, an object may also need methods; so in BeanShell, methods may also contain methods at any level. Here is a simple example:

 user( n ) {
 name = n;
 reset( ) {
 print( "Reset user:"+name );
 }
 return this; // return user as object
 }
 bob = user("Bob" );
 print( bob.name ); // "Bob"
 bob.reset( ); // prints "Reset user: Bob"


This example assigns the context of the user( ) method to the variable bob and refers to the field bob.name and the method bob.reset( ). If you find this strange, don't worry. The most common reason you'd want to script an object is to implement a Java interface, and you can do that using the standard Java anonymous inner class syntax, as we'll discuss next or just use a regular class. BeanShell gives you a lot of options.

B.4.1. Scripting Interfaces and Adapters

One of the most powerful features of BeanShell is that you can "script" any interface type (provided you are running Java 1.3 or greater). BeanShell-scripted objects can automatically implement any required interface type. All you have to do is implement the necessary method (or at least the ones that are going to be invoked). You can use this feature either by explicitly referring to a BeanShell script using a this-style reference as described earlier, or by using the standard Java anonymous inner class syntax. Here is an example:

 actionPerformed( event ) { print( event ); }
 button = new JButton("Press Me!");
 button.addActionListener( this );
 frame( button );


You can type this code right on the command line and press the button to see the events it generates. In this case, the this reference refers to the current context, just as in a method. BeanShell automatically implements the ActionListener interface and delegates calls to its actionPerformed( ) method to our scripted method. Alternately, we could use the anonymous inner class syntax to create an ActionListener for our button:

 button = new JButton("Press Me!");
 button.addActionListener( new ActionListener( ) {
 actionPerformed( event ) { print( event ); }
 } );
 frame( button );


In this case the "anonymous inner class" is actually a BeanShell script that implements the ActionListener interface for us in the same way as the previous example. One more thing: we hinted earlier that you only have to implement those methods of the interface that you want to use. If you don't script a method, it's okay as long as it's not invoked (in which case, you'd get an exception). For convenience in implementing a large interface, you can define the special invoke( ) method, which handles calls to scripted methods that don't exist:

 invoke( name, args ) { print("Method: "+name+" invoked!"); }


This invoke( ) method will handle method calls for methods that are not defined and simply print their names. See the user manual for more details.

Comments