JavaRush /Java Blog /Random EN /Inner classes in local method

Inner classes in local method

Published in the Random EN group
Hello! Let's talk about another type of nested class. Namely, about local classes (Method local inner classes). The first thing you need to remember before studying is their place in the structure of nested classes. Inner classes in local method - 2Based on our diagram, we can understand that local classes are a subtype of internal classes, which we talked about in detail in one of the previous materials . However, local classes have a number of important features and differences from internal classes. The key is in their declaration: A local class is declared only in a code block. Most often - inside some method of an external class. For example, it might look like this:
public class PhoneNumberValidator {

   public void validatePhoneNumber(String number) {

        class PhoneNumber {

           private String phoneNumber;

           public PhoneNumber() {
               this.phoneNumber = number;
           }

           public String getPhoneNumber() {
               return phoneNumber;
           }

           public void setPhoneNumber(String phoneNumber) {
               this.phoneNumber = phoneNumber;
           }
       }

       //...code валидации номера
   }
}
IMPORTANT!This code will not compile when pasted into IDEA if you have Java 7 installed. We will talk about the reasons for this at the end of the lecture. In a few words, the work of local classes is highly dependent on the language version. If this code does not compile for you, you can either switch the language version in IDEA to Java 8, or add a word finalto the method parameter so that it looks like this: validatePhoneNumber(final String number). After this everything will work. This is a small program - a phone number validator. Its method validatePhoneNumber()takes a string as input and determines whether it is a phone number. And inside this method we declared our local class PhoneNumber. You might have a logical question: why? Why declare a class inside a method? Why not use a regular inner class? Indeed, one could do this: make the class PhoneNumberinternal. Another thing is that the final decision depends on the structure and purpose of your program. Let's remember our example from the lecture about inner classes:
public class Bicycle {

   private String model;
   private int mawWeight;

   public Bicycle(String model, int mawWeight) {
       this.model = model;
       this.mawWeight = mawWeight;
   }

   public void start() {
       System.out.println("Go!");
   }

   public class HandleBar {

       public void right() {
           System.out.println("Steering wheel to the right!");
       }

       public void left() {

           System.out.println("Steering wheel to the left!");
       }
   }
}
In it we made HandleBar(the handlebars) an internal class of the bike. What's the difference? First of all, in using the class. The class HandleBarfrom the second example is a more complex entity than PhoneNumberfrom the first. First, y HandleBarhas public methods rightand left(are not setter and getter). Secondly, we cannot predict in advance where Bicyclewe may need it and its external class - these can be dozens of different places and methods, even within the same program. But with a class PhoneNumbereverything is much simpler. Our program is very simple. It has only one function - to check whether the number is a phone number. In most cases, ours PhoneNumberValidatorwill not even be an independent program, but simply a part in the authorization logic for the main program. For example, on various websites, when registering, you are often asked to enter a phone number. And if you type some nonsense instead of numbers, the site will display an error: “This is not a phone number!” For the operation of such a site (or rather, the user authorization mechanism), its developers can include an analogue of ours in the code PhoneNumberValidator. In other words, we have one external class with one method that will be used in one place in the program and nowhere else. And if it does, then nothing will change in it: one method does its job - that’s all. In this case, since all the work logic is collected in one method, it will be much more convenient and correct to encapsulate an additional class there. It does not have its own methods other than getter and setter. We essentially only need the constructor data from it. It is not used in other methods. Therefore, there is no reason to extend information about it beyond the single method where it is used. We gave an example of declaring a local class in a method, but this is not the only possibility. It can be declared simply in a code block:
public class PhoneNumberValidator {

   {
       class PhoneNumber {

           private String phoneNumber;

           public PhoneNumber(String phoneNumber) {
               this.phoneNumber = phoneNumber;
           }
       }

   }

   public void validatePhoneNumber(String phoneNumber) {


       //...code валидации номера
   }
}
Or even in a loop for!
public class PhoneNumberValidator {


   public void validatePhoneNumber(String phoneNumber) {

       for (int i = 0; i < 10; i++) {

           class PhoneNumber {

               private String phoneNumber;

               public PhoneNumber(String phoneNumber) {
                   this.phoneNumber = phoneNumber;
               }
           }

           //...Howая-то логика
       }

       //...code валидации номера
   }
}
But such cases are extremely rare. In most cases, the declaration will still occur inside the method. So, we dealt with the announcement, we also talked about “philosophy” :) What other features and differences do local classes have from internal classes? A local class object cannot be created outside the method or block in which it is declared. Imagine we need a method generatePhoneNumber()that generates a random phone number and returns a PhoneNumber. We will not be able to create such a method in our validator class in the current situation:
public class PhoneNumberValidator {

   public void validatePhoneNumber(String number) {

        class PhoneNumber {

           private String phoneNumber;

           public PhoneNumber() {
               this.phoneNumber = number;
           }

           public String getPhoneNumber() {
               return phoneNumber;
           }

           public void setPhoneNumber(String phoneNumber) {
               this.phoneNumber = phoneNumber;
           }
       }

       //...code валидации номера
   }

   //ошибка! компилятор не понимает, что это за класс - PhoneNumber
   public PhoneNumber generatePhoneNumber() {

   }

}
Another important feature of local classes is the ability to access local variables and method parameters. In case you forgot, “local” is a variable declared inside a method. String russianCountryCodeThat is, if we create a local variable inside a method for some of our purposes validatePhoneNumber(), we can access it from the local class PhoneNumber. However, there are a lot of subtleties here that depend on the version of the language used in the program. At the beginning of the lecture, we made a note that the code in one of the examples may not compile in Java 7, remember? Now let's look at the reasons for this :) In Java 7, a local class can only access a local variable or method parameter if they are declared in the method as final:
public void validatePhoneNumber(String number) {

   String russianCountryCode = "+7";

   class PhoneNumber {

       private String phoneNumber;

       //ошибка! параметр метода должен быть объявлен How final!
       public PhoneNumber() {
           this.phoneNumber = number;
       }

       public void printRussianCountryCode() {

           //ошибка! локальная переменная должна быть объявлена How final!
           System.out.println(russianCountryCode);
       }

   }

   //...code валидации номера
}
Here the compiler threw two errors. But here everything is in order:
public void validatePhoneNumber(final String number) {

   final String russianCountryCode = "+7";

    class PhoneNumber {

       private String phoneNumber;


       public PhoneNumber() {
           this.phoneNumber = number;
       }

       public void printRussianCountryCode() {

           System.out.println(russianCountryCode);
       }

    }

   //...code валидации номера
}
Now you know the reason why the code at the beginning of the lecture did not compile: a local class in Java 7 only has access to final-method parameters and final-local variables. In Java 8, the behavior of local classes has changed. In this version of the language, the local class has access not only to final-local variables and parameters, but also to effective-final. Effective-finalis a variable whose value has not changed since initialization. For example, in Java 8 we can easily display a variable to the console russianCountryCode, even if it is not final. The main thing is that it does not change its meaning. In this example, everything works as it should:
public void validatePhoneNumber(String number) {

  String russianCountryCode = "+7";

    class PhoneNumber {

       public void printRussianCountryCode() {

           //в Java 7 здесь была бы ошибка
           System.out.println(russianCountryCode);
       }

    }

   //...code валидации номера
}
But if we change the value of the variable immediately after initialization, the code will not compile.
public void validatePhoneNumber(String number) {

  String russianCountryCode = "+7";
  russianCountryCode = "+8";

    class PhoneNumber {

       public void printRussianCountryCode() {

           //error!
           System.out.println(russianCountryCode);
       }

    }

   //...code валидации номера
}
But it’s not for nothing that a local class is a subtype of an internal class! They also have common points. A local class has access to all (even private) fields and methods of the outer class: both static and non-static. For example, let's add a static field to our validator class String phoneNumberRegex:
public class PhoneNumberValidator {

   private static String phoneNumberRegex = "[^0-9]";

   public void validatePhoneNumber(String phoneNumber) {
       class PhoneNumber {

           //......
       }
   }
}
Validation will be performed using this static variable. The method checks whether the string passed to it contains characters that do not match the regular expression " [^0-9]" (that is, the character is not a number from 0 to 9). We can easily access this variable from the local class PhoneNumber. For example, write a getter:
public String getPhoneNumberRegex() {

   return phoneNumberRegex;
}
Local classes are similar to inner classes because they cannot define or declare any static members. Local classes in static methods can only reference static members of the enclosing class. For example, if you do not define a variable (field) of the enclosing class as static, the Java compiler generates an error: “A non-static variable cannot be referenced from a static context.” Local classes are not static because they have access to the containing block's instance members. Therefore, they cannot contain most types of static declarations. You cannot declare an interface inside a block; Interfaces are static in nature. This code won't compile:
public class PhoneNumberValidator {
   public static void validatePhoneNumber(String number) {
       interface I {}

       class PhoneNumber implements I{
           private String phoneNumber;

           public PhoneNumber() {
               this.phoneNumber = number;
           }
       }

       //...code валидации номера
   }
}
But if an interface is declared inside an outer class, the class PhoneNumbercan implement it:
public class PhoneNumberValidator {
   interface I {}

   public static void validatePhoneNumber(String number) {

       class PhoneNumber implements I{
           private String phoneNumber;

           public PhoneNumber() {
               this.phoneNumber = number;
           }
       }

       //...code валидации номера
   }
}
Local classes cannot declare static initializers (initialization blocks) or interfaces. But local classes can have static members, provided they are constant variables ( static final). That's what they are, local classes! As you can see, they have many differences from internal classes. We even had to dive into the features of the language version to understand how they work :) In the next lecture, we'll talk about anonymous inner classes - the last group of nested classes. Good luck with your studies! :)
Comments
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION