9

I was playing with some code to make a "closure like" construct ( not working btw )

Everything looked fine but when I tried to access a final local variable in the code, the exception InstantiationException is thrown.

If I remove the access to the local variable either by removing it altogether or by making it class attribute instead, no exception happens.

The doc says: InstantiationException

Thrown when an application tries to create an instance of a class using the newInstance method in class Class, but the specified class object cannot be instantiated. The instantiation can fail for a variety of reasons including but not limited to:

- the class object represents an abstract class, an interface, an array class, a primitive type, or void

- the class has no nullary constructor

What other reason could have caused this problem?

Here's the code. comment/uncomment the class attribute / local variable to see the effect (lines:5 and 10 ).

import javax.swing.*;
import java.awt.event.*;
import java.awt.*;
class InstantiationExceptionDemo {
     //static JTextField field = new JTextField();// works if uncommented

    public static void main( String [] args ) {
        JFrame frame = new JFrame();
        JButton button = new JButton("Click");
        final JTextField field = new JTextField();// fails if uncommented

        button.addActionListener( new _(){{
            System.out.println("click " + field.getText());
        }});
    
        frame.add( field );
        frame.add( button, BorderLayout.SOUTH );
        frame.pack();frame.setVisible( true );
    
    }
}
class _ implements ActionListener {
    public void actionPerformed( ActionEvent e ){
        try {
            this.getClass().newInstance();
        } catch( InstantiationException ie ){
            throw new RuntimeException( ie );
        } catch( IllegalAccessException ie ){
            throw new RuntimeException( ie );
        }
    }
}

Is this a bug in Java?

edit

Oh, I forgot, the stacktrace ( when thrown ) is:

Caused by: java.lang.InstantiationException: InstantiationExceptionDemo$1
at java.lang.Class.newInstance0(Class.java:340)
at java.lang.Class.newInstance(Class.java:308)
at _.actionPerformed(InstantiationExceptionDemo.java:25)
7
  • What line is the exception thrown on? Commented May 25, 2010 at 17:40
  • 25: this.getClass().newInstance() Commented May 25, 2010 at 17:41
  • @Oscar: I'm confused about the syntax of the anonymous inner class. Is this supposed to be the constructor? Commented May 25, 2010 at 17:58
  • @Bozho: Doesn't the initializer block of an anonymous class need to be static? (I've never really tried this). Commented May 25, 2010 at 20:19
  • @Uri. There are two types of initializer blocks, class initializer and instance initializers, as with everything else, the first uses static and the former doesn't. The effect, is, the initializer block is executed before the constructor. It is not widely used because there's constructors in first place, while the static {} construct is the alternative to initialize class resources because there are not "class" constructors. Commented May 25, 2010 at 20:38

3 Answers 3

8

Well, that makes sense.

Only your first instance of the _ class has access to the local variable. Subsequent instances can't, unless you provide them with it (via constructor arg)

Constructor[] constructor = a.getClass().getDeclaredConstructors();
for (Constructor c : constructors) {
     System.out.println(c.getParameterTypes().length);
}

outputs 1. (a is the instance of your anonymous class)

That said, I don't think this is a good way to implement closures. The initializer block is called at least once, without the need of it. I assume you are just playing around, but take a look at lambdaj. Or wait for Java 7 :)

Sign up to request clarification or add additional context in comments.

2 Comments

+1 Mmmhh yes and no. In the case of regular anonymous inner classes the compiler creates the reference of the local final without having to have a constructor or an argument in the method, I understand from your loop, that, the compiler created it for me. Probably the compiler should've set it for me also in the initializer block
About the implementation of closures, well, is not only "not a good way" because, well.. it doesn't work at all. This was just an experiment. For real code I would use the "accepted" closure idiom for Java which is, using an anonymous inner class new ActionListener(){public void actionPerformed(ActionEvent e){}}
6

Here's an excerpt of the javap -c InstantiationExceptionDemo$1 of the static field version:

Compiled from "InstantiationExceptionDemo.java"
class InstantiationExceptionDemo$1 extends _{
InstantiationExceptionDemo$1();
  Code:
   0:   aload_0
   1:   invokespecial   #8;  //Method _."<init>":()V
   4:   getstatic       #10; //Field InstantiationExceptionDemo.field:
                             //Ljavax/swing/JTextField;

And here's the javap -c InstantiationExceptionDemo$1 of the final local variable version:

Compiled from "InstantiationExceptionDemo.java"
class InstantiationExceptionDemo$1 extends _{
InstantiationExceptionDemo$1(javax.swing.JTextField);
  Code:
   0:   aload_0
   1:   invokespecial   #8; //Method _."<init>":()V
   4:   aload_1

So there's your cause: the final local variable version needs an extra argument, the JTextField reference, in the constructor. It has no nullary constructor.

This makes sense if you think about it. Else, how is this version of InstantiationExceptionDemo$1 going to get the field reference? The compiler hides the fact that this is given as a parameter to the synthetic constructor.

2 Comments

+1 I didn't knew actually how the final local variable were "inlined" in anonymous classes. According to what you write, they are passed in the syntactic constructor, right? So, the reflective invocation need then to also pass one parameter more. A great instructive answer.
+1 I didn't knew the compiler did that. I which I could mark two answers as accepted. I know what to do, I'll upvote another of your answers :P ;)
1

Thank you both Bozho and Polygenlubricants for the enlightening answers.

So, the reason is ( in my own words )

When using a local final variable, the compiler creates a constructor with the fields used by the anonymous inner class and invokes it. It also "inject" field with the values.

So, what I did, was to modify my creation to load the right constructor with the correct values using reflection.

This is the resulting code:

import javax.swing.*;
import java.awt.event.*;
import java.awt.*;
import java.lang.reflect.*;

class InstantiationExceptionDemo {

    public static void main( String [] args ) {

        JFrame frame = new JFrame();
        final JButton reverse = new JButton("Reverse");
        final JButton swap    = new JButton("Swap");

        final JTextField fieldOne = new JTextField(20);
        final JTextField fieldTwo = new JTextField(20);

        // reverse the string in field one
        reverse.addActionListener( new _(){{
            StringBuilder toReverse = new StringBuilder();
            toReverse.append( fieldOne.getText() );
            toReverse.reverse();
            fieldOne.setText( toReverse.toString() );

            //fieldOne.setText( new StringBuilder( fieldOne.getText() ).reverse().toString() );
        }});

        // swap the fields 
        swap.addActionListener( new _(){{
            String temp = fieldOne.getText();
            fieldOne.setText( fieldTwo.getText() );
            fieldTwo.setText( temp  );
        }});

        // scaffolding
        frame.add( new JPanel(){{
            add( fieldOne );
            add( fieldTwo );
        }} );
        frame.add( new JPanel(){{
            add( reverse );
            add( swap );
        }}, BorderLayout.SOUTH );
        frame.pack();frame.setVisible( true );

    }
}
abstract class  _ implements ActionListener {
    public _(){}

    public void actionPerformed( ActionEvent e ){ 
        invokeBlock();
    }

    private void invokeBlock(){
    // does actually invoke the block but with a trick
    // it creates another instance of this same class
    // which will be immediately discarded because there are no more 
    // references to it. 
        try {
            // fields declared by the compiler in the anonymous inner class
            Field[] fields = this.getClass().getDeclaredFields();
            Class[] types= new Class[fields.length];
            Object[] values = new Object[fields.length];
            int i = 0;
            for( Field f : fields ){
                types[i] = f.getType();
                values[i] = f.get( this );
                i++;
            }
            // this constructor was added by the compiler
            Constructor constructor = getClass().getDeclaredConstructor( types );
            constructor.newInstance( values );

        } catch( InstantiationException ie ){
            throw new RuntimeException( ie );
        } catch( IllegalAccessException ie ){
            throw new RuntimeException( ie );
        }catch( InvocationTargetException ie ){
            throw new RuntimeException( ie );        
        } catch(NoSuchMethodException nsme){
            throw new RuntimeException( nsme );
        }
    }
}

Of course, as Bozho points out, this is not a good way ( not it is a way, but not a good one ) to create closures.

There are two problems with this.

1.- The initializer block is invoked when it is declared.

2.- There is no way get the parameters of the actual code ( ie actioneEvent in actionPerformed )

If we could just delay the execution of the initializer block this would make a nice ( in terms of syntax ) closure alternative.

Perhaps in Java 7 :(

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.