class OuterClass {
...
static class StaticNestedClass {
...
}
class InnerClass {
...
}
}
Тези вътрешни класове се наричат вложени. Те са разделени на 2 вида:
- Нестатични вложени класове. Те се наричат още вътрешни класове.
- Статични вложени класове.
- местен клас
- анонимен клас
public class Bicycle {
private String model;
private int weight;
public Bicycle(String model, int weight) {
this.model = model;
this.weight = weight;
}
public void start() {
System.out.println("Let's go!");
}
public class Handlebar {
public void right() {
System.out.println("Steer right!");
}
public void left() {
System.out.println("Steer left!");
}
}
public class Seat {
public void up() {
System.out.println("Seat up!");
}
public void down() {
System.out.println("Seat down!");
}
}
}
Тук имаме Bicycle
класа. Има 2 полета и 1 метод: start()
. Той се различава от обикновения клас по това, че съдържа два класа: Handlebar
и Seat
. Техният code е написан вътре в Bicycle
класа. Това са пълноценни класове: Howто можете да видите, всеки от тях има свои собствени методи. В този момент може да имате въпрос: защо, за бога, бихме поставor един клас в друг? Защо да ги правим вътрешни класове? Е, да предположим, че имаме нужда от отделни класове за понятията кормило и седалка в нашата програма. Разбира се, не е необходимо да ги правим вложени! Можем да правим обикновени класове. Например така:
public class Handlebar {
public void right() {
System.out.println("Steer right!");
}
public void left() {
System.out.println("Steer left");
}
}
public class Seat {
public void up() {
System.out.println("Seat up!");
}
public void down() {
System.out.println("Seat down!");
}
}
Много добър въпрос! Разбира се, ние не сме ограничени от технологията. Това със сигурност е вариант. Тук по-важното е правилният дизайн на часовете от гледна точка на конкретна програма и нейната цел. Вътрешните класове са за отделяне на обект, който е неразривно свързан с друг обект. Кормилото, седалките и педалите са компоненти на велосипеда. Отделно от велосипеда нямат особен смисъл. Ако направим всички тези концепции отделни публични класове, щяхме да имаме codeа като този в нашата програма:
public class Main {
public static void main(String[] args) {
Handlebar handlebar = new Handlebar();
handlebar.right();
}
}
Хм... Значението на този code дори е трудно за обяснение. Имаме няHowво неясно кормило (Защо е необходимо? Нямам представа, честно казано). И тази дръжка се върти надясно... съвсем сама, без велосипед... незнайно защо. Разделяйки концепцията за кормилото от концепцията за велосипеда, загубихме известна логика в нашата програма. Използвайки вътрешен клас, codeът изглежда много различно:
public class Main {
public static void main(String[] args) {
Bicycle peugeot = new Bicycle("Peugeot", 120);
Bicycle.Handlebar handlebar = peugeot.new Handlebar();
Bicycle.Seat seat = peugeot.new Seat();
seat.up();
peugeot.start();
handlebar.left();
handlebar.right();
}
}
Конзолен изход:
Seat up!
Let's go!
Steer left!
Steer right!
Сега това, което виждаме, изведнъж придобива смисъл! :) Създадохме обект велосипед. Създадохме два велосипедни „подобекта“ — кормило и седалка. Повдигнахме седалката за удобство и тръгнахме: педалите и управлението според нуждите! :) Методите, от които се нуждаем, се извикват на съответните обекти. Всичко е просто и удобно. В този пример отделянето на кормилото и седалката подобрява капсулирането (скриваме данни за частите на велосипеда в съответния клас) и ни позволява да създадем по-подробна абстракция. Сега нека разгледаме една различна ситуация. Да предположим, че искаме да създадем програма, която симулира магазин за велосипеди и резервни части за велосипеди. В тази ситуация предишното ни решение няма да работи. В магазин за велосипеди всяка отделна част от велосипед има смисъл дори когато е отделена от велосипед. Например, ще ни трябват методи като „продай педали на клиент“, „купи нова седалка“ и т.н. Би било грешка да използваме вътрешни класове тук — всяка отделна велосипедна част в нашата нова програма има meaning, което стои на собствен: може да бъде отделен от концепцията за велосипед. Това е точно това, на което трябва да обърнете внимание, ако се чудите дали да използвате вътрешни класове or да организирате всички обекти като отделни класове. Обектно-ориентираното програмиране е добро с това, че улеснява моделирането на обекти от реалния свят. Това може да бъде вашият ръководен принцип, когато решавате дали да използвате вътрешни класове. В истински магазин, резервните части са отделни от велосипедите - това е добре. Това означава, че е добре и при проектирането на програма. Добре, разбрахме "философията" :) Сега нека се запознаем с важни "технически" характеристики на вътрешните класове. Ето Howво определено трябва да запомните и разберете:
-
Обект от вътрешен клас не може да съществува без обект от външен клас.
Това има смисъл: затова направихме
Seat
иHandlebar
вътрешни класове в нашата програма — за да не се окажем с осиротели кормила и седалки.Този code не се компorра:
public static void main(String[] args) { Handlebar handlebar = new Handlebar(); }
Друга важна характеристика следва от това:
-
Обект от вътрешен клас има достъп до променливите на външния клас.
Например, нека добавим
int seatPostDiameter
променлива (представляваща диаметъра на колчето за седалка) към нашияBicycle
клас.След това във
Seat
вътрешния клас можем да създадемdisplaySeatProperties()
метод, който показва свойствата на седалката:public class Bicycle { private String model; private int weight; private int seatPostDiameter; public Bicycle(String model, int weight, int seatPostDiameter) { this.model = model; this.weight = weight; this.seatPostDiameter = seatPostDiameter; } public void start() { System.out.println("Let's go!"); } public class Seat { public void up() { System.out.println("Seat up!"); } public void down() { System.out.println("Seat down!"); } public void displaySeatProperties() { System.out.println("Seat properties: seatpost diameter = " + Bicycle.this.seatPostDiameter); } } }
И сега можем да покажем тази информация в нашата програма:
public class Main { public static void main(String[] args) { Bicycle bicycle = new Bicycle("Peugeot", 120, 40); Bicycle.Seat seat = bicycle.new Seat(); seat.displaySeatProperties(); } }
Конзолен изход:
Seat properties: seatpost diameter = 40
Забележка:новата променлива се декларира с най-стриктния модификатор за достъп (
private
). И все пак вътрешният клас има достъп! -
Обект от вътрешен клас не може да бъде създаден в статичен метод на външен клас.
Това се обяснява със специфичните особености на организацията на вътрешните класове. Вътрешен клас може да има конструктори с параметри or само конструктор по подразбиране. Но независимо от това, когато създаваме обект от вътрешен клас, препратка към обекта от външния клас невидимо се предава на създадения обект от вътрешния клас. В крайна сметка наличието на такава референция към обект е абсолютно изискване. В противен случай няма да можем да създаваме обекти от вътрешния клас.
Но ако метод от външния клас е статичен, тогава може да нямаме обект от външния клас! И това би било нарушение на логиката на функционирането на една вътрешна класа. В тази ситуация компилаторът ще генерира грешка:
public static Seat createSeat() { // Bicycle.this cannot be referenced from a static context return new Seat(); }
-
Вътрешен клас не може да съдържа статични променливи и методи.
Логиката е същата: статичните методи и променливи могат да съществуват и да бъдат извиквани or препращани дори при липса на обект.
Но без обект от външния клас, няма да имаме достъп до вътрешния клас.
Явно противоречие! Ето защо статичните променливи и методи не са разрешени във вътрешните класове.
Компилаторът ще генерира грешка, ако се опитате да ги създадете:
public class Bicycle { private int weight; public class Seat { // An inner class cannot have static declarations public static void displaySeatProperties() { System.out.println("Seat properties: seatpost diameter = " + Bicycle.this.seatPostDiameter); } } }
-
Когато създавате обект от вътрешен клас, неговият модификатор за достъп е важен.
Вътрешен клас може да бъде маркиран със стандартните модификатори за достъп:
public
,private
,protected
иpackage private
.Защо това има meaning?
Това засяга къде можем да създаваме екземпляри на вътрешния клас в нашата програма.
Ако нашият
Seat
клас е деклариран катоpublic
, можем да създавамеSeat
обекти във всеки друг клас. Единственото изискване е да съществува и обект от външния клас.Между другото, вече направихме това тук:
public class Main { public static void main(String[] args) { Bicycle peugeot = new Bicycle("Peugeot", 120); Bicycle.Handlebar handlebar = peugeot.new Handlebar(); Bicycle.Seat seat = peugeot.new Seat(); seat.up(); peugeot.start(); handlebar.left(); handlebar.right(); } }
Лесно получихме достъп до
Handlebar
вътрешния клас отMain
класа.Ако декларираме вътрешния клас като
private
, ще можем да създаваме обекти само във външния клас.Вече не можем да създадем
Seat
обект "отвън":private class Seat { // Methods } public class Main { public static void main(String[] args) { Bicycle bicycle = new Bicycle("Peugeot", 120, 40); // Bicycle.Seat has private access in Bicycle Bicycle.Seat seat = bicycle.new Seat(); } }
Сигурно вече разбирате логиката :)
-
Модификаторите за достъп за вътрешните класове работят по същия начин като за обикновените променливи.
Модификаторът
protected
осигурява достъп до променлива на екземпляр в подкласове и класове, които са в същия пакет.protected
работи и за вътрешни класове. Можем да създавамеprotected
обекти от вътрешния клас:- във външния клас;
- в неговите подкласове;
- в класове, които са в същия пакет.
Ако вътрешният клас няма модификатор за достъп (
package private
), могат да се създават обекти от вътрешния клас:- във външния клас;
- в класове, които са в същия пакет.
Вие сте запознати с модификаторите от дълго време, така че тук няма проблеми.
GO TO FULL VERSION