Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why am I having this InstantiationException in Java when accessing final local variables?

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)
like image 378
OscarRyz Avatar asked May 25 '10 17:05

OscarRyz


People also ask

How can Java Lang InstantiationException be resolved?

How to Resolve InstantiationException. To avoid the InstantiationException , it should be ensured that the instance of the class that is attempted to be created at runtime using Class. newInstance() is a concrete class and not an abstract class, interface, array class, primitive or void.

What are local variables in Java?

A local variable in Java is a variable that's declared within the body of a method. Then you can use the variable only within that method. Other methods in the class aren't even aware that the variable exists. If we are declaring a local variable then we should initialize it within the block before using it.

What are class variables called in Java?

Class variables also known as static variables are declared with the static keyword in a class, but outside a method, constructor or a block.


2 Answers

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 :)

like image 162
Bozho Avatar answered Nov 12 '22 17:11

Bozho


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.

like image 33
polygenelubricants Avatar answered Nov 12 '22 19:11

polygenelubricants