Why can't a static class be instantiated


10.1 Local and anonymous classes



10.1.1 Basics

Up to JDK 1.0, classes were always defined at the package level; nesting was not possible. This simplified the compiler implementation and made the structure of the classes within a package flat and clear. This concept always became unwieldy when a class only had local significance or when "a small class quickly" was needed. Since there are no function pointers in Java, the only way to exchange small code parts between different program parts is to declare an interface and make the required functionality available in an implementing class. This technique was introduced in Section 9.4.3.

Particularly with the extensions for the programming of graphical user interfaces, which were introduced with JDK 1.1, the desire for a more flexible mechanism arose. Due to the new event model (see chapter 28), since JDK 1.1, it is much more often that small program parts have to be written that only have a meaning in a very limited context. The solution to this problem was created with the introduction of local and anonymous classes (in the JDK they are called Inner classes designated). A new class Y is defined within an existing class X, which is only visible within X. Object instances of Y can thus only be created within X. The other way around, Y can access the member variables of X.

Local and anonymous classes are a powerful - and sometimes confusing - feature of Java. We want to present its most important applications below. In addition, there are seldom used variants, which mainly result from the tricky application of modifiers to the local class or its members. We do not want to go into this further here.

10.1.2 Non-static local classes

Classes in classes

The definition of a non-static local class corresponds exactly to the basic principle described above: within the definition part of any class defines a new class. They must be instantiated within the outer class, i.e. in one of the methods of the outer class or during its initialization. The inner class can access the member variables of the outer class and vice versa. The following listing illustrates this with a simple example:

001 / * Listing1001.java * / 002 003 class Outer 004 {005 String name; 006 int number; 007 008 public void createAndPrintInner (String iname) 009 {010 Inner inner = new Inner (); 011 inner.name = iname; 012 System.out.println (inner.getQualifiedName ()); 013} 014 015 class Inner 016 {017 private String name; 018 019 private string getQualifiedName () 020 {021 return number + ":" + Outer.this.name + "." + name; 022} 023} 024} 025 026 publicclass Listing1001 027 {028 publicstaticvoid main (String [] args) 029 {030 Outer outer = new Outer (); 031 outer.name = "Outer"; 032 outer.number = 77; 033 outer.createAndPrintInner ("Inner"); 034} 035}Listing1001.java
Listing 10.1. A non-static local class

First a class with the member variables and is defined. A class is then defined within. It has its own member variable and a method. When the program starts, an instance of is created and its member variables are initialized. Then it calls the method.

An instance of is now created in and initialized with the name passed as an argument. The instantiation takes place in the context of the outer class, and this can access the member variable of the inner class. In practice, however, it is more important to let the inner class access the member variables of the outer class. This makes the status of the outer class accessible to it and it can generate program code (and, by encapsulating it in an object, transfer it to a completely different place in the program if necessary) that uses information from the environment of the class definition. To show this, call the method of the inner class.

Member variables are accessed in three different ways. When using unqualified Names (i.e. those without a class name prefix) lexical Visibility rules applied. The compiler first checks whether there is a local variable with this name. If this is not the case, it looks for a member variable with the same name in the current class. If this is also not available, he gradually expands his search from the inside out to all enclosing classes. In the example, the member variable of the same name denotes and that of. If the member variable of an outer class is covered by a member variable of the same name of the inner class, the prefix "Class name.this." the outer variable can be accessed. This is done for the variable in the example program.

If the expression "ClassName.this" is used alone, it designates the object of the outer class in which the current instance of the inner class was created. In would therefore designate the instance created in line 030.

The implementation of local classes could be done in JDK 1.1 without major changes to the virtual machine. Local classes are known at compile time, but are treated like normal classes at runtime. In particular, the compiler creates a separate .class file for each local class. To avoid duplication, their name consists of the name of the outer class followed by a dollar sign and the name of the inner class. In the anonymous classes discussed later, a sequential number assigned by the compiler is used instead of the name of the inner class. When translating the previous example, the class files Outer.class, Outer $ Inner.class and Listing1001.class would be generated.

Classes in methods

Inner classes can be defined not only at the outermost level of another class, but also within their methods and even within any block. In this case you can also access the local variables of the surrounding method or the surrounding block. The condition is, however, that these have been declared as constant with the help of the keyword.

This way of defining local classes is not very common, but it sometimes appears in other code. In practice, mostly anonymous classes are used in their place, as discussed in the following section. Still, let's look at a simple example:

001 / * Listing1002.java * / 002 003 class Outer2 004 {005 publicvoid print () 006 {007 finalint value = 10; 008 009 class Inner2 010 {011 publicvoid print () 012 {013 System.out.println ("value =" + value); 014} 015} 016 017 Inner2 inner = new Inner2 (); 018 inner.print (); 019} 020} 021 022 publicclass Listing1002 023 {024 publicstaticvoid main (String [] args) 025 {026 Outer2 outer = new Outer2 (); 027 outer.print (); 028} 029}Listing1002.java
Listing 10.2. Definition of a local class in a method

10.1.3 Anonymous classes

The most common use of local classes within methods is to use them anonymous define. The class does not have its own name, but definition and instantiation take place in a combined statement. An anonymous class is therefore a one-way class that can only be instantiated once. Anonymous classes are usually derived from other classes or extend existing interfaces. Their main application is in the definition of Listeners for graphical interfaces, which we will discuss in detail in Chapter 28.

As a simple application example we want to use the interface defined in Listing 9.13 again and show how to create an anonymous class when calling and pass it on as an argument:

001 / * Listing1003.java * / 002 003 publicclass Listing1003 004 {005 publicstaticvoid printTable (DoubleMethod meth) 006 {007 System.out.println ("Value table" + meth.toString ()); 008 for (double x = 0.0; x <= 5.0; x + = 1) {009 System.out.println ("" + x + "->" + meth.compute (x)); 010} 011} 012 013 publicstaticvoid main (String [] args) 014 {015 printTable (016 new DoubleMethod () 017 {018 publicdouble compute (double value) 019 {020 return Math.sqrt (value); 021} 022} 023) ; 024} 025}Listing1003.java
Listing 10.3. Using Anonymous Classes

Instead of instantiating a predefined class, a local anonymous class is defined and instantiated in line 016. Syntactically it differs from the instantiation of a predefined class in that it is not followed by a semicolon, but by a curly bracket. This is followed by the definition of the class. Used as a class name interface specified, the anonymous class implements this interface. If, on the other hand, it is the name of one class, the anonymous class is derived from it. In our example the interface is implemented.

The use of the anonymous class has not necessarily made the program clearer. In fact, both the utility and syntax of anonymous classes have been the subject of much discussion. The great advantage of anonymous classes is their flexibility. An anonymous class can be declared where it is needed (here, for example, when calling). It can also pass on code that accesses local variables and member variables in its immediate environment.

Its use should be limited to those cases in which a class has to be created with a few lines of code that is only relevant at a certain point in the program. If the class is more extensive or if it is required in different places, a named class should be defined and instantiated at the call points.

10.1.4 Static local classes

The last variant of inner classes that we want to consider is actually not one at all. A class is defined within another class and given the attribute. In this case, the compiler generates code that corresponds exactly to that of an ordinary global class. In particular, a static local class is not only visible within the class in which it was defined, but can also be instantiated from outside. It also has no reference to the instantiating class and therefore cannot access its member variables. The only difference to a global class is that the name of the inner class contains the name of the outer class as a prefix. Both are separated from each other by a point.

For example, a class could be defined as a static local class if its raison d'etre is based on the existence of the outer class. Typical applications are smaller auxiliary classes that have enough substance to declare their own class, but too little for their own file. Thanks to the separate namespace, they can also have common names such as "Entry", "Element" or "Debug".

The following listing shows a simple application of static local classes:

001 / * Listing1004.java * / 002 003 class Outer3 004 {005 staticclass Inner3 006 {007 publicvoid print () 008 {009 System.out.println ("Inner3"); 010} 011} 012} 013 014 publicclass Listing1004 015 {016 publicstaticvoid main (String [] args) 017 {018 Outer3.Inner3 inner = new Outer3.Inner3 (); 019 inner.print (); 020} 021}Listing1004.java
Listing 10.4. Using Static Local Classes

Local and anonymous classes are used quite often. Further examples of their application can also be found in this book. For example, Section 28.2.2 explains how to use them to develop event handlers for GUI programs. Another example of their application can be found in Section 15.4, which explains the implementation of an iterator (see below).