Doskwiera Wam problem utrzymywania referencji powiązanych ze sobą obiektów w Javie? Może ja coś na to poradzę i zaprezentuję jak klasy wewnętrzne mogą częściowo rozwiązać ten dylemat? Częściowo, ponieważ ta metoda nie będzie służyć mocno rozbudowanym klasom posiadającym ponad dwieście linijek. Chyba, że potraficie się odnaleźć w kodzie źródłowym przyjmującym horrendalne rozmiary. Zapraszam!

KLASY WEWNĘTRZNE ŚRODKIEM NA ZACHOWANIE REFERENCJI

Termin "referencja" obcy? Sięgajcie tutaj. "Pierwsze słyszę!" przy klasach wewnętrznych? Prosimy tutaj.

Nie napisałem Wam wszystkiego o klasach wewnętrznych. Generalnie są one przydatne przy odbieraniu zdarzeń poprzez interfejsy. Co nie znaczy, że tylko ten problem potrafią rozwiązać. Drugi "sens" z ich częstego korzystania to konieczność utrzymania referencji z obiektem, którego dane składowe mają być wykorzystywane w drugiej klasie, która ona sama niekoniecznie musi pochodzić z tego samego drzewa dziedziczenia.

Dajmy na to, że dysponujemy jakimś punktem w układzie współrzędnych, który ma później uzależniać pozycję prostokąta rysowanego na panelu. Można bawić się w zmienne statyczne, gettery i settery czy też w osobne klasy i dołączanie takiego punktu jako parametru konstruktora, ale można też utworzyć klasy wewnętrzne, które będą rozszerzać "JPanel" i w ten sposób również nie naruszamy zasad hermetyzacji.

Jest tylko jedno "ale". W przypadku dużej ilości linijek kodu źródłowego, takie osadzanie klasy w klasie będzie powodować sporo bałaganu, gdyż wszystko musi być w jednym pliku. Nie da się zrobić klasy wewnętrznej w osobnym pliku, a jeśli się da, to z pewnością nie będzie to tak samo traktowane i będą jakieś różnice. Zatem, jedyna porada jest taka, abyście stosowali techniki pisania dobrego kodu i pisali go profesjonalnie.

KOD ŹRÓDŁOWY Z ZACHOWANIEM REFERENCJI PRZEZ KLASY WEWNĘTRZNE

Jeśli teoretyczne uzasadnienie nie jest żadnym uzasadnieniem, łapcie się za przykład kodu źródłowego wykorzystującego powyższą sytuację razem z komponentami biblioteki "Swing":

  • KLASA "Main"
public class Main
{
	public static void main(String[] args)
	{
		new GUI();
	}
}
  • KLASA "Entity"
import java.awt.*;

public class Entity
{
	private int x, y, width, height;

	public Entity(int x, int y, int width, int height)
	{
		this.x = x;
		this.y = y;
		this.width = width;
		this.height = height;
	}

	public void draw(Graphics2D g2D)
	{
		int centerX = (x - width) >> 1;
		int centerY = (y - height) >> 1;

		g2D.fillRect(centerX, centerY, width, height);
	}
}
  • KLASA "GUI"
import javax.swing.*;
import java.awt.*;

public class GUI
{
	private Entity a, b;

	public GUI()
	{
		createInstances();
	}

	private void createInstances()
	{
		a = new Entity(50, 60, 32, 32);
		b = new Entity(560, 400, 80, 48);

		new GUIFrame();
	}

	class GUIFrame extends JFrame
	{
		private GUIPanel guiPanel;

		public GUIFrame()
		{
			super("GUI");
			createInstances();
			addToPanel();
			setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
			setVisible(true);
			setResizable(false);
			setSize(640, 480);
		}

		private void createInstances()
		{
			guiPanel = new GUIPanel();
		}

		private void addToPanel()
		{
			add(BorderLayout.CENTER, guiPanel);
		}

		class GUIPanel extends JPanel
		{
			@Override
			protected void paintComponent(Graphics g)
			{
				Graphics2D g2D = (Graphics2D) g;

				super.paintComponent(g);
				a.draw(g2D);
				b.draw(g2D);
			}
		}
	}
}

Klasa "GUI" posiada klasy wewnętrzne dla "ramki" i panelu "Swing". Zaczniemy najpierw od klasy "Entity". Zawarłem w niej informacje odnośnie pozycji i wymiarów oraz metodę rysującą, aby uniknąć pisania metod pobierających wszystkie cztery wartości osobno. Pojedyncze wywołanie "fillRect" oraz dostosowanie współrzędnych tak, aby traktowało się je jako środek obiektu, a nie jego lewy górny róg. Skorzystałem z przesunięcia bitowego jako najbardziej przyjazny dla komputera zapis dzielenia przez dwa.

Klasa "GUI" jest najciekawsza ze wszystkich. Ona sama zawiera jedynie referencje do naszych dwóch obiektów. Przy pomocy funkcji "createInstances", tworzymy sobie nasze prostokąty przy użyciu konstruktorów. Potem tworzymy obiekt klasy "GUIFrame", klasy wewnętrzne również mogą się "rozmnażać". Nie potrzeba żadnych przypisań, gdyż nie wykonuje się na tym obiekcie żadnych innych operacji poza zakres tej metody.

Konstruktor tej samej klasy jest wykonywany jako kolejny krok "łańcucha". W trakcie wykonywania wszystkich zawartych tam instrukcji, wywołuje się znowu metodę "createInstances", ale pragnę zaznaczyć, iż to jest już ZUPEŁNIE INNA metoda której definicja pobierana jest z bieżącej klasy "GUIFrame", nie "GUI"! To jest drugi cenny wniosek. Nazwy mogą się powielać, nie ma żadnych przeszkód. "JFrame" musi być "nosicielem" instancji klasy "JPanel", zatem idzie kolejna klasa wewnętrzna, "GUIPanel".

Tutaj jest już konieczne zawarcie zmiennej prywatnej dla naszego panelu z powodu wykorzystywania tego samego obiektu w dwóch różnych metodach ("createInstances" oraz "addToPanel"), chociaż można by na upartego to wcisnąć w jedną linijkę zamieniając zmienną na ciąg ze słowem "new". Kolejny fakt jest taki, iż klasy wewnętrzne mogą mieć wiele poziomów "schodzenia w głąb".

Na samym końcu zostaje "JPanel", który posiada jedynie w swoim bloku przesłonięcie metody "paintComponent", metody rysującej po panelu. Dopiero wtedy proces inicjalizacji zostaje zakończony, a po chwili widzimy dwa prostokąty narysowane na panelu zgodnie z podanymi współrzędnymi. Ciekawe zjawisko, które również wymaga trochę drapania się po głowie.


To wszystko z zakresu tłumaczenia. Klasy wewnętrzne mogą być jak najbardziej używane do utrzymywania łączności między obiektami. O ile nie są one rozbudowane na pięćset linijek, potrafią dobrze komunikować się poprzez referencje w sposób polimorficzny. Polecam sobie samemu coś takiego napisać, chociaż raz w życiu!

PODOBNE ARTYKUŁY