Ogólne
Rozwiązania zadań należy przysłać na mój adres (olekz@mimuw itd.)
- Wystarczą same pliki .java
- Jeśli by mieli Państwo kłopot z załączeniem plików .java, możecie zmienić im rozszerzenie na .txt
(wolę to niż dostać kod wklejony do treści maila)
Zadanie 1
(termin: 18 III)
Wersja "trudniejsza" zadania 5 ze scenariusza 3 na Ważniaku (link). Szczegóły:
- Możesz założyć, że każda linia wejścia zawiera imię i nazwisko jednej osoby.
- Program powinien zakończyć działanie, gdy na wejściu pojawi się pusta linia.
Przydatne klasy i metody:
- klasa Scanner, metoda nextLine i podobne
- metoda equals klasy String
Zadanie 2
(zadane przez wykładowcę, termin: 26 III ?)
Znajduje się na Moodle'u.
Zadanie 3
(termin: 1 IV)
Zaimplementuj w całości metodę dodaj klasy Liczba, którą zajmowaliśmy się na zajęciach.
Twój program powinien zawierać:
- klasę Liczba;
- konstruktor Liczba(String) oraz metodę String toString();
- metody Liczba powiększO(Liczba) (modyfikującą obiekt this i zwracającą odwołanie do niego) oraz Liczba dodaj(Liczba) (niczego niemodyfikującą, ale zwracającą odwołanie do nowego obiektu);
- procedurę void suma(String, String) (poza klasą Liczba albo statyczną w tej klasie), która pobiera dwa napisy, konwertuje je na Liczby, dodaje je i wypisuje wynik na System.out;
- procedurę main, zawierającą przykład wykorzystania procedury suma.
Zadanie 4
(termin: 8 IV)
Zaimplementuj klasy i metody umożliwiające przetwarzanie wyrażeń algebraicznych zależących od jednej zmiennej x, w następującym zakresie:
(lista wymagań jest długa, ale większość z tego zrobiliśmy na zajęciach - jednak spisuję dla tych, których nie było, albo zapomnieli)
- rodzaje wyrażeń: stała, zmienna (tzn. wystąpienie x), 4 działania (+ - * /), sinus, kosinus
- dostępne operacje:
- tworzenie - klasy powinny udostępniać rozsądne konstruktory
- obliczanie wartości w danym punkcie - pobiera wartość zmiennej x, zwraca wartość całego wyrażenia dla danej wartości x
- obliczanie pochodnej wyrażenia - zwraca wyrażenie reprezentujące pochodną
- obliczanie całki oznaczonej - pobiera końce przedziału całkowania, zwraca przybliżoną wartość całki
(nie chodzi tu o śrubowanie dokładności wyniku - po prostu uwzględnij wartości wyrażenia w więcej niż kilku punktach)
- wypisywanie (najlepiej jako metoda toString) - tutaj zwróć uwagę na nawiasowanie, a konkretnie:
- przede wszystkim na to, żeby nawiasy były wszędzie tam, gdzie powinny być, np. sin (x + 2.1), 2.5 - (3.5 - 4.5)
- w drugiej kolejności na to, żeby nie było ich tam, gdzie są niepotrzebne, np. sin (x), 2.5 + (3.5 + 4.5)
- wymagania dot. obiektowości kodu:
- każdy z podanych rodzajów wyrażeń (stała, dodawanie, sinus itp.) powinien być opisany przez osobną klasę
- z drugiej strony, wszystkie wyrażenia powinny być obiektami pewnej klasy głównej (a klasy szczegółowe być jej podklasami) - zalecałbym nawet bardziej rozbudowaną hierarchię, np. klasę pośrednią "operacja dwuargumentowa"
- klasa główna "wyrażenie" powinna udostępniać metody (być może typu abstract) obliczające wartość/pochodną/całkę
- a ogólnie: przestrzegaj dobrych zwyczajów (w szczególności chowaj jako private lub protected, co tylko nadaje się do schowania)
- funkcja main powinna zawierać:
- wypisanie pochodnej wyrażenia x * x * x * x oraz jej wartości dla x = 2
- wypisanie wyrażenia sin x / (x * cos x) oraz jego całki oznaczonej na przedziale [0.1, 0.4]
(jeśli korzystasz z parsera, możesz w tym celu zamiast metody dzialaj() użyć metody parsuj(zapis), która zwraca wyrażenie zbudowane na podstawie zapisu podanego jako argument, a nie pobranego z konsoli - oczywiście wciąż obowiązuje konwencja prefiksowa, np. parsuj(" ' **xx*xx"))
Zadanie 5
(termin: 25 IV)
Zaimplementuj klasy pozwalające na przechowywanie bazy danych osób i sortowanie ich wg różnych atrybutów:
- wymagane klasy/interfejsy:
(nie musisz trzymać się podanych tutaj nazw)
- Osoba - udostępnia co najmniej:
- konstruktor Osoba(String imię, String nazwisko, Date dataUrodz, float pensja)
(najlepiej by było jak powyżej, ale jeśli masz już gotowy konstruktor przyjmujący atrybuty typu String i tłumaczący je na Date/Float, to też może być)
- metodę toString()
- BazaOsób - przechowuje ciąg osób, udostępnia co najmniej:
- konstruktor bezparametrowy
- metodę dodaj(Osoba o)
- metodę sortuj(Porównywacz p), sortującą osoby zgodnie z porządkiem określanym przez p, zaimplementowaną "ręcznie"
- metodę sortuj2(Porównywacz p), działającą jak wyżej, zaimplementowaną poprzez metody sortujące biblioteki Javy
- metodę toString() (alternatywnie może być wypisz())
- Porównywacz - abstrakcyjny typ (być może interfejs), którego instancje będą używane do porównywania osób, udostępnia:
- metodę mniejsze(Osoba a, Osoba b), zwracającą wartość typu boolean
- PorównywaczNazwisk, PorównywaczDatUrodz, PorównywaczPensji - trzy podklasy rozszerzające/implementujące Porównywacz
- wymagania dot. wykorzystania biblioteki Javy:
- w implementacji metody sortuj2 użyj narzędzia umożliwiającego sortowanie obiektów danego typu wg danego porządku (określonego przez progamistę)
- zadbaj, żeby rozszerzenie programu o metodę sortuj2 wykorzystało wcześniejszy kod, dokonując w nim możliwie niewielkich zmian
- wskazówka z zajęć: można użyć np. Collections.sort
- użyj odpowiedniej klasy do zarządzania datami (konwersji z/do typu String oraz porównywania)
- na zajęciach nie była podana nazwa tej klasy - znalezienie jej jest częścią zadania (polecam Java 7 API tudzież StackOverflow)
- spróbuj nie używać przestarzałych metod (deprecated) - informacje na temat statusu metod i proponowanych zamienników znajdziesz w Java 7 API; w razie konfuzji przy numerach wersji pamiętaj, że "Java N" jest obecnie synonimem dla "Java 1.N"
Zadanie 6
(termin: 14 V)
Zaimplementuj różne rodzaje generatorów i filtrów, pozwalające m. in. na sortowanie danych z wejścia oraz obliczanie liczb pierwszych.
Elementy omówione dość gruntownie na zajęciach: (spisuję dla przypomnienia / wyjaśnienia itp.)
- Generator generuje liczby, dopóki ma na to ochotę.
Udostępnia metody:
- getNext() (wynik typu int)
- hasNext() (wynik typu boolean)
Rodzaje generatorów:
- GeneratorKons - wczytuje liczby z konsoli (np. przy pomocy Scannera); kończy pracę, gdy skończą się liczby na wejściu (np. gdy metoda hasNextInt() dla skanera zwróci false)
- GeneratorNat - generuje kolejne liczby naturalne
(jeśli będzie Ci tak wygodniej, możesz kazać mu generować liczby tylko w określonym przedziale, np. do 100, albo od 2 do 100, a potem zakończyć pracę)
- Filtr to takie pudełko, do którego można wrzucić liczbę, i na ogół wypadnie z niego jakaś liczba (być może inna), która wędruje do następnego filtra itd.
Udostępnia metody:
- zjedz(int) - obsługuje nadejście nowej liczby, na ogół wywołując rekurencyjnie metodę zjedz() na następnym filtrze;
zwraca pewien Filtr - na ogół samego siebie - który kod wywołujący powinien podstawić w miejsce wołanego filtra
- wypisz() - wypisuje zawartość tego filtra oraz - rekurencyjnie - następujących po nim
Rodzaje filtrów:
- FiltrP - służy do sortowania: pamięta jedną liczbę a i kiedy otrzymuje b, zapisuje w sobie mniejszą z liczb a, b, a większą przekazuje dalej
- FiltrD - podobna zasada, ale obsługuje podzielność: pamięta a i kiedy otrzymuje b, to nic nie robi, jeśli b jest wielokrotnością a, a w przeciwnym razie przepuszcza b dalej
- zaślepka (na zajęciach dwie klasy: ZaślepkaP i ZaślepkaD, odpowiadające dwóm powyższym rodzajom filtrów) - specjalny filtr na końcu łańcucha; kiedy otrzymuje liczbę b, podstawia na swoje miejsce nowy filtr odpowiedniego typu o zawartości b, za którym znajduje się zaślepka (być może ta sama)
- Funkcja wykonaj(generator, filtr):
- Może być zdefiniowana np. jako metoda statyczna w głównej klasie programu
- Pobiera kolejne liczby z generatora i przekazuje je do filtra (który zapewne w międzyczasie się rozbudowuje)
- Na koniec prosi filtr, żeby się wypisał
- Oczekiwane przykłady wykorzystania procedury wykonaj (mniej więcej):
- wykonaj(new GeneratorKons(), new ZaślepkaP()) pobiera z konsoli ciąg liczb całkowitych i wypisuje go w wersji posortowanej (z powtórzeniami)
- wykonaj(new GeneratorNat(2, 100), new ZaślepkaD()) wypisuje kolejne liczby pierwsze mniejsze równe od 100
Dodatkowe elementy zadania:
- Stwórz generator losowy (GeneratorLos), który generuje losowe liczby naturalne w pewnym przedziale
(przedział najlepiej określić w momencie tworzenia takiego generatora)
- Stwórz filtr unifikujący (FiltrU), który:
- działa podobnie do filtra porównującego FiltrP, z tym, że usuwa powtórzenia, tzn. jeśli a == b, to nie przekazuje dalej żadnej liczby
- udostępnia dodatkowe metody, które pozwalają na przerwanie wykonania programu, gdy łańcuch filtrów osiągnie żądaną długość
(może na przykład pamiętać liczbę wszystkich filtrów następujących po nim - choć na zajęciach omówiliśmy też inne, chyba lepsze sposoby)
- oczywiście zadbaj o zaślepkę dla nowego rodzaju filtra
Uwaga: kod procedury wykonaj powinien nie rozróżniać rodzajów filtrów. Wobec tego może się okazać, że jeśli zdefiniujesz nowe metody dla klasy FiltrU, to przynajmniej część z nich trzeba będzie udostępnić również w klasie Filtr i odpowiednio zaimplementować dla innych rodzajów filtrów.
- Użyj nowych klas w taki sposób, aby metoda wykonaj umożliwiała:
- wykonanie losowania totolotka (tj. wypisanie losowego ciągu rosnącego 6 elementów z przedziału [1..49])
- pobranie z konsoli ciągu liczb całkowitych i wypisanie go na ekran w wersji posortowanej z usuniętymi powtórzeniami
- Bonus - za dodatkowe 0,3 punktu: zmodyfikuj swój kod tak, żeby wszystkie zaślepki były obsługiwane przez tylko jedną klasę Zaślepka z jednym konstruktorem; konstruktor ten powinien jako argument przyjmować klasę filtra, dla której należy stworzyć zaślepkę (np. FiltrP.class)
Zadanie 7
(termin: 20 V)
Napisz klasę Pair<K, V>, implementującą interfejs Entry.Map<K, V>, oraz dwie klasy implementujące interfejs Map<K, V>:
- ListMap<K, V> - implementacja oparta o ArrayList (wyszukiwanie poprzez przechodzenie po liście, w czasie liniowym)
- TreeMap<K, V> - implementacja oparta o TreeSet (taka, żeby get, put, remove zajmowały czas logarytmiczny)
Utwórz też wspólną abstrakcyjną nadklasę dla ListMap oraz TreeMap i umieść w niej jak najwięcej kodu wspólnego dla obu klas.
(Przykładowo, plik TreeMap.java można napisać tak, by wszystkie konstruktory i metody miały łącznie 12 linii - a pewnie da się mieć też mniej).
W klasie głównej napisz jakąś procedurę, wykonującą dowolnie wybrane przez Ciebie operacje na obiekcie typu Map<String, Integer> i wykonaj ją na new ListMap<>() oraz new TreeMap<>(). Byłoby najpiękniej, gdyby było widać różnicę w czasie działania, ale tego już nie wymagam.
Wskazówki ogólne:
- W obu przypadkach wewnętrzna kolekcja (typu ArrayList tudzież TreeSet) powinny przechowywać wpisy mapy jako obiekty typu Pair<K, V>.
- Zwróć uwagę, że wiele metod udostępnianych przez interfejs Map można bardzo łatwo (np. w jednej linii) sprowadzić do innych spośród nich.
- Zwróć też uwagę że niektóre potrzebne metody mają wspólny kod, który można "wyłączyć przed nawias" tworząc własne metody prywatne. W szczególności:
- metody get, put, remove możesz uproscić przy pomocy dodatkowej metody find(key), która zwraca parę (key, wartośćDlaKey) lub null w przypadku nieistnienia takowej pary;
- kiedy wykonasz powyższe, kod metod put i remove dla klas ListMap oraz TreeMap okaże się nieznacznie różny, choć bardzo podobny. Spróbuj uprościć również tę sytuację, wykorzystując dziedziczenie.
Wskazówki dotyczące TreeMap:
- W przypadku klasy TreeMap możesz dodać w kodzie wymaganie, aby klasa kluczy K implementowała interfejs Comparable.
- TreeSet<E> umie szybko wyszukiwać elementy dzięki temu, że elementy klasy E są uporządkowane. U nas rolę E ma pełnić Pair<K, V>. To prowadzi do kilku pytań:
- jaki porządek chciał(a)byś wprowadzić w klasie Pair<K, V>, żeby wykorzystująca go implementacja TreeMap działała jak należy?
(wskazówka: porównuj tylko klucze)
- w jaki sposób przekazać informację o wybranym porządku do klasy TreeSet?
(można to zrobić na dwa sposoby - obejrz listę dostępnych konstruktorów dla TreeSet)
- (kwestia czysto estetyczna, ale i to miewa znaczenie:) który z tych dwóch sposobów jest bardziej naturalny?
- W klasie TreeSet mogą Ci się przydać metody tailSet oraz first.
Zadanie 8
(termin: 27 V)
To zadanie jest prostsze niż poprzednie. (Mój kod zajmuje mniej miejsca niż treść zadania. I chyba też krócej był pisany).
(Na czerwono późniejsze zmiany w treści zadania).
Zadanie: Napisz usprawnienia dla wykorzystywania iteratorów - w obie strony:
- kod, który wyposaża iterator w pamięć (tak, że można "podejrzeć" ostatni element zwrócony przez next(), a także element, który ma być zwrócony za chwilę)
- kod, który tworzy iterator na podstawie samej procedury next() napisanej w sposób beztroski (jeśli nie ma elementu, to zwraca null lub rzuca wyjątek)
Szczegóły: Twoje rozwiązanie powinno zawierać:
- Klasę abstrakcyjną PrawieIterator, udostępniającą metody:
- next() - abstrakcyjna, zwraca następny element - a jeśli go nie ma, to ma prawo zwrócić null lub rzucić NoSuchElementException;
- makeIterator() - zwraca poprawny iterator obsługujący ten sam strumień wartości - a więc dostarczający metodę hasNext() oraz gwarancję, że dopóki hasNext() zwraca true, wywołanie next() zwraca rozsądną następną wartość
- Klasę Wczytywacz, rozszerzającą PrawieIterator, w której next() pobiera liczbę całkowitą z wejścia przy pomocy Scannera.
Metoda powinna zwracać null, jeśli wczytaną liczbą jest 0. Poza tym może działać beztrosko (nie przejmować się wyjątkami rzucanymi przez Scanner.nextInt()).
- Klasę IteratorPamiętliwy, implementującą interfejs Iterator, a ponadto udostępniającą:
- konstruktor przyjmujący zwykły iterator;
- metodę seePrevious(), zwracającą element ostatnio zwrócony przez next(), nie zmieniając stanu iteratora (pamiętliwego);
(jeśli next() nie było jeszcze ani razu wywołane, niech seePrevious() rzuca NoSuchElementException)
- metodę seeNext(), zwracającą element, który ma być zwrócony przez najbliższe next(), nie zmieniając stanu iteratora (pamiętliwego).
(jeśli hasNext() zwraca false, niech seeNext() rzuca NoSuchElementException)
- Procedurę main(), która:
- pobiera z wejścia ciąg liczb, aż do zakończenia wejścia lub napotkania zera;
- wypisuje wczytane liczby kolejno na ekran;
- do każdej liczby oprócz pierwszej dołącza informację, o ile jest ona większa/mniejsza od poprzedniej;
- do działania wykorzystuje obiekt klasy Wczytywacz, przerobiony na iterator pamiętliwy przy pomocy obu opisanych powyżej mechanizmów naraz;
- nie zawiera zmiennych lokalnych innych niż opisany powyżej iterator.
(innymi słowy - nie spamiętuj liczb zwracanych przez iterator, przecież on pamięta je sam)
Przykład: Jeśli na wejściu podano ciąg 2, 4, -1, 0, wyjście powinno wyglądać na przykład tak:
2
4; zmiana o 2
-1; zmiana o -5
Uwagi:
- Użyj klas uogólnionych, gdziekolwiek to rozsądne.
- Nie musisz implementować metody remove() dla iteratorów (jeśli używasz NetBeans/Eclipse, możesz pozostawić implementację domyślną).
- Przetestuj swoje rozwiązanie zarówno interaktywnie (czyli uruchamiając program w NetBeans/Eclipse lub w konsoli), jak i na zewnętrznym pliku (przekierowując wejście przy pomocy "<") - czasem programy działają w jednym z tych scenariuszy, a w drugim nie.
- Zwróć uwagę, żeby program kończył działanie (a nie zawieszał się) po wpisaniu zera na wejściu.
Wskazówki:
- Nie proś o element, jeśli powiedziano Ci, że go nie ma. (To może zawiesić program).
- NoSuchElementException jest podklasą RuntimeException, więc można go rzucać bez deklarowania tego w nagłówku funkcji.
- A może oba rodzaje konwerterów iteratorów można jakoś ujednolicić?
- Być może umili Ci życie dostępna w Javie składnia definiowania podklas "na poczekaniu" przy okazji tworzenia ich instancji. Na przykład ponizszy kod tworzy na poczekaniu klasę kota (choć jest ona nienazwana, więc nie można jej użyć w żaden inny sposób niż w związku z konkretnym obiektem mruczek):
public abstract class Zwierzę {
public abstract String odglos();
}
// ...
Zwierzę mruczek = new Zwierzę() { // uwaga 1: Zwierzę mogłoby tu równie dobrze być interfejsem!
@Override
public String odglos() {
return "miau";
}
}; // uwaga 2: cały napis od słowa "new" do tej klamry włącznie jest zwykłym wyrażeniem,
// więc można z nim dużo zrobić, np. wywołać na nim metodę, albo przekazać do jakiejś funkcji
// (może to jedynie wymagać otoczenia go zwykłymi okrągłymi nawiasami)
Zresztą, jeśli napiszesz samo new Zwierzę()
(co bez wykorzystania poniższej sztuczki nie ma sensu, bo Zwierzę jest klasą abstrakcyjną), to np. NetBeans zaproponuje Ci automatyczne wygenerowanie szablonu związanego z tą właśnie składnią.
Zadanie 9
(termin: 3 VI)
Tradycyjnie: treść bardzo długa, a rozwiązanie krótkie. (Wersję podstawową da się zmieścić poniżej 50 linii).
Tekst czerwony stanowi bonus (dodatkowe 0,3 punktu) - w wersji podstawowej zadania po prostu go pomiń.
Użyj mechanizmu refleksji dla zbudowania interpretera prostych poleceń Javy (ściślej: prawie-Javy). A dokładniej:
- Pobierz i umieść w projekcie trzy pliki: ReflectionCaller, ExtendedReflectionCaller, Interpreter.
(Pliki spodziewają się, że trafią do pakietu o nazwie z9, jeśli chcesz, możesz to zmienić).
- Przygotuj własną implementację interfejsu ReflectionCaller.
(Znaczenie wszystkich metod w tym interfejsie jest opisane w komentarzach w jego kodzie).
- Niech Twoja klasa implementuje również rozszerzony interfejs ExtendedReflectionCaller.
- Jako treść głownej procedury wpisz new Interpreter(new NazwaTwojejImplementacji()).run();
Powinno umożliwić to przeprowadzenie przykładowej rozmowy. W razie wątpliwości uznaj, że treść zadania nie wymaga większej ogólności niż ten przykład.
Uwagi do treści zadania:
- Obsłuż wyjątki rzucane przez Twoją implementację ReflectionCallera tak, aby wystąpienie wyjątku nie kończyło działania programu, lecz powodowało wypisanie do strumienia System.err komunikatu zawierającego rodzaj wyjątku oraz jakąś (choćby ogólnikową) informację o sytuacji, w której wystąpił.
Dzięki temu, jeśli użytkownik interpretera popełni błąd, będzie mógł zaraz wpisać poprawione polecenie.
- Jeśli wystąpi wyjątek, masz prawo wypisać kilka komunikatów o błędzie; pierwszy z nich powinien być rozsądny, a następne już nie muszą. (Tak jak np. w kompilatorze Pascala)
- W przypadku polecenia wywołania metody bez wyniku (typ wyniku void), Twoja implementacja może rzucić wyjątek VoidResultException lub zwrócić null. Możesz zrobić, jak Ci wygodniej (choć dla wygody obsługi interpretera zalecałbym to pierwsze).
Wskazówki:
- Polecam slajdy z moodle'a, tutoriale Oracle'a o refleksji oraz strony API dla klas Class i Method
- Zwróć uwagę, że wiele metod danej klasy może mieć tę samą nazwę (polimorfizm!).
W związku z tym, żeby uzyskać właściwą metodę, trzeba w ogólności użyć typów argumentów.
- Co gorsza, faktyczne typy argumentów mogą się różnić od zadeklarowanych.
Na przykład jeśli wywołujemy na obiekcie klasy HashMap (bez parametrów typowych, czyli realnie HashMap<Object, Object>) metodę put("a", 1),
to typami faktycznymi argumentów są String i Integer, ale typami zadeklarowanymi są Object i Object, ponieważ korzystamy tu z metody put przyjmującej w ogólności argumenty takich właśnie typów.
Zła wiadomość jest taka, że metoda getMethod w klasie Class odnajduje metody wyłącznie na podstawie typu zadeklarowanego - a więc np. wywołanie getMethod("put", String.class, Integer.class) zwróci błąd. Toteż odradzam używania tej metody.
Spróbuj zamiast tego przejrzeć wszystkie dostępne metody i sprawdzić (przy pomocy odpowiednich metod bibliotecznych), która z nich ma odpowiednią nazwę oraz zadeklarowane typy argumentów kompatybilne z faktycznymi typami podanych argumentów.
- Zwróć też uwagę na nazewnictwo klas: o ile w kodzie piszesz zazwyczaj po prostu "HashMap" (dzięki wcześniejszemu import java.util.*), o tyle mechanizm refleksji oczekuje (przynajmniej domyślnie) pełnej nazwy klasy, a więc "java.util.HashMap". Twoja implementacja metody classInstantiate powinna przyjmować krótką nazwę i sobie z nią radzić (np. próbując dopisać z przodu "java.lang." albo "java.util.")
- Być może przyda Ci się też wiedza, że Integer.class to nie to samo, co int.class. I tak dalej.
Ale pamiętaj, że nie wymagam poprawnego działania programu dla metod pobierających argumenty typów prymitywnych (int, float itd.).
- Niektóre atrybuty i metody użyte w przykładzie w wersji rozszerzonej są statyczne - mogą wymagać szczególnego traktowania!
Żeby było jasne, w co się można bawić, opiszę na koniec składnię interpretera.
(Tu nie ma się czego bać - nic z tym nie musisz robić, bo to już napisane :)
Na niebiesko zaznaczam elementy składni, które odwołują się do interfejsu ReflectionCaller - a więc jedynie te elementy będą korzystać z Twojego rozwiązania wersji podstawowej.
- wyrażenie może być postaci:
zmienna
- zwraca wartość zmiennej o podanej nazwie
- nazwa zmiennej musi rozpoczynać się od małej litery (i być różna od
new
)
- zmiennych nie trzeba deklarować; zmienne niezainicjowane mają wartość
null
stała
- obsługiwane są stałe typu int oraz String (te drugie oczywiście podajemy w cudzysłowach)
obiekt[i]
- zwraca i-tą pozycję tablicy (o ile obiekt jest faktycznie tablicą)
obiekt.metoda(argumenty)
- zwraca wynik wywołania metody
new klasa
- zwraca nowy obiekt podanej klasy (uzyskany z konstruktora bezparametrowego)
obiekt.atrybut
- zwraca wartość atrybutu
klasa
- zwraca podaną klasę (ją samą, przydatne np. w System.ciąg_dalszy
)
- polecenie może być postaci:
wyrażenie
- wypisuje na ekranie wartość wyrażenia (chyba że jest nią void
)
exit
- powoduje zakończenie pracy programu
zmienna = wyrażenie
- przypisuje wartość wyrażenia do zmiennej
obiekt.atrybut = wyrażenie
- przypisuje wartość wyrażenia atrybutowi obiektu
- dodatkowo polecenie można zakończyć średnikiem (żeby było bardziej Javowo)
Pierwsze dwa warianty poleceń są w zasadzie potrzebne jedynie w wersji podstawowej rozwiązania; w wersji rozszerzonej można je zastąpić równoważnymi System.out.println(...)
oraz System.exit(0)
.
Zadanie 10
(termin: 17 VI)
Dokończ - w elegancki sposób obiektowy - parser SAX z naszych przedostatnich zajęć.
- Przykładowe dane wejściowe
- Parsując plik, zapamiętuj kolejne osoby na liście osób (klasa Osoba)
- Po zakończeniu parsowania wypisz osoby na ekran
- Aby uniknąć powtórzeń w kodzie parsera, stwórz klasę ObsługiwaczDanych (lub z inną nazwą), która:
- Jest odpowiedzialna za dodanie do bazy osób (a ściślej: do obecnie wczytywanej osoby) informacji pochodzącej z metody characters parsera
- W zależności od rodzaju przetwarzanego tagu, może być obsługiwaczem imion, pensji itp., lub nawet (dla nieistotnych tagów) obsługiwaczem trywialnym
(a więc potrzebne są podklasy - żeby nie mnożyć bytów, polecam definiowanie podklas na poczekaniu)
- Użycie refleksji nie będzie nagradzane ani karane - Twój wybór
- Uwaga: przykładowe dane wejściowe z zajęć zostały troszeczkę utrudnione: dodałem datę urodzenia pierwszego pracownika, która powinna być przez parser zignorowana.
Bonus (dodatkowe 0,3 pt): Obsłuż plik o bogatszym formacie, w którym pracownicy są pogrupowani w hierarchię oddziałów (tag <dept>
, zawierający tag <name>
).
- Przykładowe dane wejściowe dla bonusu
- To chyba jasne, ale napiszę explicite: tym razem "baza danych" powinna zawierać obiekty klas Osoba oraz Oddział, odpowiednio powiązane
- W tej wersji informacje wypisane przez program powinny mieć postać (przykład nawiązuje do danych z poprzedniego punktu):
THE COMPANY
DEPT 1
... // informacje o pracowniku
DEPT 2
... // informacje o pracowniku
Nie wnikam w szczegóły, ale zależy mi na czytelności struktury, którą (subiektywnie) rozumiem tak, że dla oddziałów powinny być osobne linie i wcięcia.