4. Tablice "obiektowe"
Jak wiemy już, i jak widzieliśmy przed chwilą - tablice mogą zawierać referencje do dowolnych obiektów.
Składniowo wygląda to identycznie jak operowanie na tablicach liczb
całkowitych czy rzeczywistych. Wydaje się, że nie ma różnicy. public class TabPar { public static void main(String[] args) { Para[] tabPar = new Para[10]; for (int i=0; i < tabPar.length; i++) tabPar[i].show("Para " + (i+1)); } } Ten program skompiluje się bezbłędnie, ale przy jego wykonaniu otrzymamy następujący komunikat. Exception in thread "main" java.lang.NullPointerException
at TabPar.main(TabPar.java:6)
Jest on skutkiem odwołania do nieistniejącego obiektu - wywołania metody
show z klasy Para za pomocą referencji o wartości null. public class TabPar { public static void main(String[] args) { Para[] tabPar = new Para[10]; for (int i=0; i < tabPar.length; i++) tabPar[i] = new Para(i+1, i+2); for (int i=0; i < tabPar.length; i++) tabPar[i].show("Para " + (i+1)); } } co w wyniku da następujący wydruk: Para 1 ( 1 , 2 )
Para 2 ( 2 , 3 ) Para 3 ( 3 , 4 ) Para 4 ( 4 , 5 ) Para 5 ( 5 , 6 ) Para 6 ( 6 , 7 ) Para 7 ( 7 , 8 ) Para 8 ( 8 , 9 ) Para 9 ( 9 , 10 ) Para 10 ( 10 , 11 )
To co się dzieje wyjaśnia następujący rysunek. ![]()
Można przypuszczać, że w przypadku takiej klasy jak Para nie zdarzy nam się
błąd "braku obiektów" (bowiem zwykle będziemy chcieli mieć jakieś konkretne
pary i będziemy pamiętać, że trzeba je stworzyć za pomocą wyrażenia new). Tworzenie tablic z elementami oznaczającycmi obiekty - podsumowanie
(na przykładzie klasy Button)
Naturalnie, również w przypadku tablic referencji do obiektów możemy
użyć inicjatorów klamrowych, które pozwalają - przy deklaracji - stworzyć
i zanicjować tablicę. Robiliśmy to już zresztą przy okazji inicjacji tablicy
miast literałami łańcuchowymi. Elementy tablicy mogą zawierać referencje wskazujące na obiekty różnych
klas, pod warunkiem, że klasy te dziedziczą tę samą klasę - określającą ogólny,
niejako wspólny dla wszystkich, typ elementów tablicy
Dość zawikłaną treść najlepiej wyjaśni schematyczny przykład:
Wyrażenie:
new klasaA[] { refB, refC, ... } tworzy tablicę typu klasaA[] i inicjuje ją referencjami podanymi w nawiasach klamrowych, przy czym każda z tych referencji może wskazywać obiekt klasy klasaA lub dowolnej klasy pochodnej od klasy klasaA. Wynikiem opracowania tego wyrażenia jest referencja do zmiennej tablicowej typu klasaA[]
Najczęściej wyrażenie to ma zastosowanie na liście argumentów wywołania
metody, której parametrem jest tablica. W ten sposób możemy uzyskać efekt wywołania metody ze zmienną liczbą i (do pewnego stopnia) zmiennymi typami
argumentów. public class VarArg { public VarArg() { // wywołanie metody "z dwoma" argumentami showMsgs( new String[] { "Warszawa", "Kraków" } ); // wywołanie metody "z trzema" argumemntami showMsgs( new String[] { "Ala", "Kot", "Pies" } ); // "trzy argumenty": dwie książki, jedno czasopismo showIncome( new Publication[] { new Book("P. Pies", "Psy", "WydPP", 2002, "ISBN01", 25, 100), new Book("K. Kot", "Koty", "WydPP", 2002, "ISBN02", 22, 90), new Journal(1, "Kwiaty", "WydAN", 2002, "ISSN03", 10, 200), } ); // "dwa argumenty": książka i czasopismo showIncome( new Publication[] { new Book("A. Koń", "Konie", "Tur", 2001, "ISBN01", 35, 50), new Journal(1, "Ryby", "W&S", 2002, "ISSN03", 20, 100), } ); } // Wypisuje w kolejnych wierszach napisy - elementy przekazanej tablicy public void showMsgs(String[] s) { for (int i=0; i<s.length; i++) System.out.println(s[i]); } // Pokazuje dochód jaki można uzyskać ze sprzedaży publikacji // przekazanych w tablicy public void showIncome(Publication[] p) { double d = 0; String opis = ""; for (int i=0; i<p.length; i++) { opis += " " + p[i].getIdent(); d += p[i].getPrice() * p[i].getQuantity(); } System.out.println("Dochód z pozycji " + opis); System.out.println(d); } public static void main(String[] args) { new VarArg(); } }
Wydruk działania programu: Warszawa Kraków Ala Kot Pies Dochód z pozycji ISBN01 ISBN02 ISSN03 6480.0 Dochód z pozycji ISBN01 ISSN03 3750.0
Zadanie. Przed lekturą dalszego tekstu proszę rozwiązać to zadanie samodzielnie
Możliwe rozwiązanie. // Modyfikacje klasy Publication public class Publication { //... private Bookshelf bs; // refrencja do półki na której stoją te publikacje // ustal półkę gdzie stoją (przy wstawianiu) public void setBookshelf(Bookshelf b) { bs = b; } // zwraca półkę, gdzie stoją te publikacje public Bookshelf whereIs() { return bs; } // ... }
W klasie Bookshelf publikacje stojące na półce będziemy rejestrować w tablicy
Publication[] pubs. Rozmiary tej tablicy określają ile można wstawić egzemplarzy
różnych publikacji. Po wstawieniu egzemplarzy jakiejś publikacji - zmienna
freeSpace (wartośc której inicjalnie równa jest rozmiarowi półki) będzie
zmniejszona o liczbę egzemplarzy. Przy wstawianiu egzemplarzy publikacji
na półkę, do tablicy pubs będziemy wpisywac referencję do wstawianej publikacji
(da nam to informacje o tym jakie publikacje są na półce). takiego wpisu
dokonujemy jednokrotnie dla publikacji (a nie dla każdego egzemplarza). W
rezultacie większość miejsca w tablicy pubs będzie niewykorzystana (zakładamy
przecież, że rozmiary tablicy określają ile można wstawić na półkę egzemplarzy).
public class Bookshelf { private int bsnr; // numer półki private Publication[] pubs; // tablica publikacji private int freeSpace; // wolne miejcce private int currIndex; // bieżący indeks tablicy publikacji, // pod tym indeksem wpiszemy referencję // do publikacji umieszczanej w tablicy Bookshelf(int nr, int size) { pubs = new Publication[size]; freeSpace = size; bsnr = nr; } // Zwraca numer półki public int getNr() { return bsnr; } // Umieszcza egzemplarze przekazanej jako argument publikacji // na półce. Zwraca true, jeśli to się powiodło - w przeciwnym razie false public boolean put(Publication p) { // jeżeli już na półce - nie robimy nic if (p.whereIs() != null) { System.out.println("Publikacje już są na półce"); return false; } int n = p.getQuantity(); // ile egzemplarzy danej pozycji wyd. if ( n > freeSpace ) { // gdy brak wolnego miejsca na półce System.out.println("Brak miejsca na półce"); return false; } freeSpace -= n; // zmniejszenie dostępnego miejsca na półce // referencja do obiektu-publikacji jest wpisywana do tablicy publikacji // indeks przesuwamy o 1 a nie o n, gdyż nie ma sensu powielać // informacji o publikacji dla wszystkich jej egzemplarzy pubs[currIndex++] = p; // w obiekcie-publikacji zapisujemy referencję do półki na której // znalazły się egzemplarze tej publikacji p.setBookshelf(this); return true; } // zwraca tablicę publikacji na półce // tablica ta ma currIndex elementów // gdyż nie powielamy informacji dla > 1 egzemplarzy // tej samej publikacji, wobec czego pozostałe elementy // tablicy publikacji pubs są null i nie uwzględniamy ich public Publication[] getPubs() { Publication[] p = new Publication[currIndex]; for (int i=0; i < currIndex; i++) p[i] = pubs[i]; return p; } }
W klasie testującej posługujemy się tablicami półek, książek i czasopism. public class BookshelfTest { public static void main(String[] args) { final int BSNUM = 5; // liczba półek, których można użyć final int BSIZE = 100; // rozmiar półki Bookshelf[] bs = new Bookshelf[BSNUM]; // tablica półek; // samych półek jeszcze nie ma Book[] bk = { new Book("P. Pies", "Psy", "WydPP", 2002, "ISBN01", 25, 100), new Book("K. Kot", "Koty", "WydPP", 2002, "ISBN02", 22, 90), new Book("A. Koń", "Konie", "Tur", 2001, "ISBN01", 35, 50), }; Journal[] jr = { new Journal(1, "Kwiaty", "WydAN", 2002, "ISSN03", 10, 20), new Journal(1, "Ryby", "W&S", 2002, "ISSN03", 20, 100), }; bs[0] = new Bookshelf(1, BSIZE); // pierwsza półka int k = 0; // bieżący indeks tablicy półek - // daje nam tę półkę na którą mamy wstawiać publikacje for (int i=0; i<bk.length; i++) // wstawiamy po kolei wszystkie książki // jeżeli nie udało się na półkę k - to bierzemy nową połkę (k+1) while (!bs[k].put(bk[i]) && k < BSNUM ) { k++; bs[k] = new Bookshelf(k+1, BSIZE); } for (int i=0; i<jr.length; i++) // wstawiamy po kolei wszystkie czasopisma // jeżeli nie udało się na półkę k - to bierzemy nową połkę (k+1) while (!bs[k].put(jr[i]) && k < BSNUM ) { k++; bs[k] = new Bookshelf(k+1, BSIZE); } // gdzie są książki ? for (int i=0; i<bk.length; i++) { Bookshelf b = bk[i].whereIs(); String s = b == null ? " nie stoi na półce" : " jest na półce "+ b.getNr(); System.out.println( bk[i].getTitle() + s); } // gdzie są czasopisma ? for (int i=0; i<jr.length; i++) { Bookshelf b = jr[i].whereIs(); String s = b == null ? " nie stoi na półce" : " jest na półce "+ b.getNr(); System.out.println(jr[i].getTitle() + s); } // co jest na półkach (tylko tych co zostały użyte) for (int i=0; i < bs.length; i++) { if (bs[i] == null) break; Publication[] p = bs[i].getPubs(); System.out.println("Półka nr " + bs[i].getNr()); for (int j=0; j < p.length; j++) System.out.println(p[j].getTitle()); } } }
Program wyprowadzi następujące wyniki.
Brak miejsca na półce Brak miejsca na półce Brak miejsca na półce Psy jest na półce 1 Koty jest na półce 2 Konie jest na półce 3 Kwiaty jest na półce 3 Ryby jest na półce 4 Półka nr 1 Psy Półka nr 2 Koty Półka nr 3 Konie Kwiaty Półka nr 4 Ryby
|