Вложени класове в Java

1. Въведение

Този урок е бързо и подробно въведение в вложените класове в езика Java.

Най-просто казано, Java ни позволява да дефинираме класове в други класове. Вложените класове ни позволяват да групираме логически класове, които се използват само на едно място, да напишем по-четим и поддържаем код и да увеличим капсулирането.

Преди да започнем, нека да разгледаме няколко вида вложени класове, налични на езика:

  • Статични вложени класове
  • Нестатични вложени класове
  • Местни класове
  • Анонимни класове

В следващите раздели ще обсъдим всеки от тях подробно.

2. Статични вложени класове

Ето няколко точки, които трябва да запомните за статичните вложени класове:

  • Както при статичните членове, те принадлежат към техния затварящ клас, а не към екземпляр на класа
  • Те могат да имат всички видове модификатори на достъп в декларацията си
  • Те имат достъп само до статични членове в затварящия клас
  • Те могат да дефинират както статични, така и нестатични членове

Нека да видим как можем да декларираме статичен вложен клас:

public class Enclosing { private static int x = 1; public static class StaticNested { private void run() { // method implementation } } @Test public void test() { Enclosing.StaticNested nested = new Enclosing.StaticNested(); nested.run(); } }

3. Нестатични вложени класове

След това, ето няколко бързи точки, които трябва да запомните за нестатичните вложени класове:

  • Те се наричат ​​още вътрешни класове
  • Те могат да имат всички видове модификатори на достъп в декларацията си
  • Подобно на променливите и методите на екземпляра, вътрешните класове са свързани с екземпляр на заграждащия клас
  • Те имат достъп до всички членове на затварящия клас, независимо дали са статични или нестатични
  • Те могат да дефинират само нестатични членове

Ето как можем да декларираме вътрешен клас:

public class Outer { public class Inner { // ... } }

Ако декларираме вложен клас с модификатор static , тогава това е статичен член. В противен случай това е вътрешен клас. Въпреки че синтактично разликата е само една ключова дума (т.е. статична ), семантично има огромна разлика между тези видове вложени класове. Вътрешните екземпляри на клас са обвързани със затварящите такива и следователно те имат достъп до своите членове. Трябва да сме наясно с този проблем, когато избираме дали да направим вложен клас да бъде вътрешен.

За да създадем екземпляр на вътрешен клас, първо трябва да инстанцираме неговия заграждащ клас.

Нека да видим как можем да направим това:

Outer outer = new Outer(); Outer.Inner inner = outer.new Inner();

В следващите подраздели ще покажем някои специални видове вътрешни класове.

3.1. Местни класове

Локалните класове са специален тип вътрешни класове - в които класът е дефиниран в метод или блок на обхвата.

Нека видим няколко точки, които трябва да запомните за този тип клас:

  • Те не могат да имат модификатори на достъп в декларацията си
  • Те имат достъп както до статични, така и до нестатични членове в заграждащия контекст
  • Те могат да определят само членове на екземпляра

Ето бърз пример:

public class NewEnclosing { void run() { class Local { void run() { // method implementation } } Local local = new Local(); local.run(); } @Test public void test() { NewEnclosing newEnclosing = new NewEnclosing(); newEnclosing.run(); } }

3.2. Анонимни класове

Анонимните класове могат да се използват за дефиниране на изпълнение на интерфейс или абстрактен клас, без да се налага да се създава многократна реализация.

Нека изброим няколко точки, които да запомните за анонимните класове:

  • Те не могат да имат модификатори на достъп в декларацията си
  • Те имат достъп както до статични, така и до нестатични членове в заграждащия контекст
  • Те могат да определят само членове на екземпляра
  • Те са единственият тип вложени класове, които не могат да дефинират конструктори или да разширяват / реализират други класове или интерфейси

За да дефинираме анонимен клас, нека първо дефинираме прост абстрактен клас:

abstract class SimpleAbstractClass { abstract void run(); }

Now let's see how we can define an anonymous class:

public class AnonymousInnerUnitTest { @Test public void whenRunAnonymousClass_thenCorrect() { SimpleAbstractClass simpleAbstractClass = new SimpleAbstractClass() { void run() { // method implementation } }; simpleAbstractClass.run(); } }

For more details, we may find useful our tutorial on Anonymous Classes in Java.

4. Shadowing

The declaration of the members of an inner class shadow those of the enclosing class if they have the same name.

In this case, the this keyword refers to the instances of the nested class and the members of the outer class can be referred to using the name of the outer class.

Let's see a quick example:

public class NewOuter { int a = 1; static int b = 2; public class InnerClass { int a = 3; static final int b = 4; public void run() { System.out.println("a = " + a); System.out.println("b = " + b); System.out.println("NewOuterTest.this.a = " + NewOuter.this.a); System.out.println("NewOuterTest.b = " + NewOuter.b); System.out.println("NewOuterTest.this.b = " + NewOuter.this.b); } } @Test public void test() { NewOuter outer = new NewOuter(); NewOuter.InnerClass inner = outer.new InnerClass(); inner.run(); } }

5. Serialization

To avoid a java.io.NotSerializableException while attempting to serialize a nested class, we should:

  • Declare the nested class as static
  • Make both the nested class and the enclosing class implement Serializable

6. Conclusion

В тази статия видяхме какво са вложени класове и различните им типове. Също така разгледахме как се различават модификаторите на видимост на полето и достъп при различните типове.

Както винаги, пълното изпълнение на този урок може да бъде намерено в GitHub.