Komputerowo

komputery, sieci, języki programowania, sieć

Fundacja Nowoczesna Polska w ramach projektu „Cybernauci – kompleksowy projekt kształtowania bezpiecznych zachowań w sieci” prowadzi Katalog ogólnodostępnych materiałów edukacyjnych z zakresu bezpiecznego korzystania z technologi informacyjno-komunikacyjnych oraz cyberbezpieczeństwa.

Na stronie www katalogu www.cybernauci.edu.pl/katalog/ znajduje się już ponad 120 ogólnodostępnych zasobów edukacyjnych!

Katalog to narzędzie, przeznaczone dla osób, które poszukują materiałów edukacyjnych dotyczących tematyki bezpieczeństwa w internecie oraz korzystania z narzędzi TIK.

Można w nim znaleźć między innymi:

  • scenariusze zajęć,
  • raporty i badania,
  • plakaty i ulotki,
  • grafiki,
  • quizy, gry
  • filmy edukacyjne

przygotowane przez różne organizacje pozarządowe, instytucje publiczne i firmy prywatne. Każdy materiał opisany jest kryteriami, obejmującymi m. in. adresatów, zakres tematyczny, licencje czy język publikacji, co pozwala na proste wyszukiwanie odpowiednich zasobów.

Katalog_materiałów_edukacyjnych_Cybernauci

Projekt „Cybernauci – kompleksowy projekt kształtowania bezpiecznych zachowań w sieci” jest finansowany ze środków Ministra Edukacji Narodowej. Projekt został objęty honorowym patronatem przez Ministerstwo Cyfryzacji, Rzecznika Praw Dziecka Marka Michalaka oraz Ośrodek Rozwoju Eduka

zasady_bezpieczenstwa_w_sieci

Obrazowo komputer można porównać do naszego mieszkania, które powinno być zabezpieczone przed włamaniem. Podobnie jak dbamy o bezpieczeństwo naszego mieszkania, tak też powinniśmy zadbać o bezpieczeństwo naszego komputera, który codziennie narażony jest na różne szkodliwe ataki. Zagrożeń jakie czyhają na nasz komputer jest mnóstwo. Często nie zdajemy sobie do końca sprawy jak jest ich wiele.

Złośliwe oprogramowanie, inaczej malware to nic innego jak różne aplikacje i skrypty, które mają szkodliwe i przestępcze bądź złośliwe działanie w stosunku do użytkowników komputerów. Warto podkreślić tutaj, że jest to głównie problem dotykający tych wszystkich komputerów, które pracują w środowisku Microsoft Windows.

Do złośliwego oprogramowania należą:

– wirusy,
– robaki,
– wabbity,
– trojany,
– backdoor,
– programy szpiegujące,
– exploit,
– rootkit,
– keylogger,
– dialery,

Do grupy tej można także zaliczyć fałszywe alarmy i żarty komputerowe, które są jednak mniej szkodliwe od złośliwego oprogramowania. Zaliczamy do nich rzekomo nowe i groźne wirusy, jak również rzekome wykrycie zainfekowanych plików, które są wywołane przez programy antywirusowe posiadające najwyższy poziom analizy heurystycznej. Żarty komputerowe najczęściej robione są początkującym użytkownikom komputerów.

Wirus atakujący nasz komputer to nic innego jak program, czy też fragment wrogiego wykonalnego kodu, który się sam dołącza, bądź zamienia na inny program w celu reprodukcji. Robi to bez zgody użytkownika. Istnieją różne rodzaje infekcji.

 

Wirusy można podzielić na:

– wirusy gnieżdżące się w sektorze rozruchowym twardego dysku,
– wirusy pasożytnicze,
– wirusy wieloczęściowe,
– wirusy towarzyszące,
– makro wirusy.

Bardo podobne do wirusów są robaki, które rozmnażają się jedynie za pośrednictwem sieci. Od wirusów różnią się tym, że nie potrzebują programu do żywienia. Najczęściej powielają się one przez pocztę elektroniczną.

Wabbit jest programem rezydentnym, który nie powiela się przez sieć. Wynik działania tego programu to jedna operacja. Przykładem może być powielenie tego samego pliku aż do wyczerpania zasobów pamięci komputera.

Zupełnie innym rodzajem oprogramowania jest trojan, który nie rozmnaża się tak jak wirus, ale jego działanie jest równie szkodliwe dla użytkownika. Trojan potrafi ukryć się pod nazwą lub częścią pliku pomocnego dla użytkownika. Potrafi on wykonać takie operacje, które w tle są szkodliwe dla użytkownika. Z pomocą trojana otwiera się port w komputerze, przez który haker może dokonać włamania.

Backdoor zaś przejmuje kontrolę nad zainfekowanym komputerem i w ten sposób umożliwia wykonanie na nim różnych czynności administracyjnych, takich jak usuwanie i zapis danych. Backdoor podszywa się pod pliki i programy, które są najczęściej używane. Haker ma możliwość administrowania systemem przez Internet i dzieje się to wbrew woli i wiedzy użytkownika.

Programy szpiegujące są oprogramowaniem, które zbiera informacje o osobie fizycznej bądź prawnej bez jej wiedzy. Szczególnie interesuje się informacjami o odwiedzanych witrynach, jak również o hasłach dostępowych. Często występują one jako dodatkowe i ukryte komponenty większego programu. Są oporne na usuwanie i jakąkolwiek ingerencję ze strony użytkownika. Szpiedzy mogą sami i bez wiedzy użytkownika pobierać i uruchamiać pliki z sieci.

Scumware jest to zbiorcze określenie oprogramowania, które wykonuje w komputerze niechciane przez użytkownika czynności,

Stealware/parasiteware służy do okradania kont internetowych,

Adware to oprogramowanie, które wyświetla reklamy,

Hijacker Browser Helper Object to dodatki do przeglądarek, które wykonują operacje bez wiedzy użytkownika.

Exploit jest kodem, który umożliwia zdalne przejęcie kontroli nad komputerem poprzez sieć i wykorzystuje w tym celu dziury w programach i systemach operacyjnych.

Rootkit jest jednym z najbardziej niebezpiecznych narzędzi używanych przez hakerów. Wykorzystywany on jest do przejęcia całkowitej kontroli nad komputerem użytkownika. Jest on bardzo trudny do usunięcia, nawet nie wystarczy w tym celu całkowite formatowanie dysku twardego. Często zagnieżdża się w pamięci flash BIOS – u płyty głównej.

Keylogger występuje w dwóch postaciach: programowej i sprzętowej. Z jego pomocą odczytywane są i zapisywane wszystkie naciśnięcia klawiszy użytkownika, co wiąże się z przechwyceniem adresów, kodów, czy innych cennych informacji w niepowołane ręce.

Dialery są szkodliwe dla posiadaczy modemów telefonicznych analogowych i cyfrowych ISDN. Przeważnie występują na stronach o tematyce erotycznej.

Na koniec warto wspomnieć o fałszywym oprogramowaniu ochronnym. Hakerzy wykorzystują naiwność internautów i często proponują fałszywe oprogramowanie ochronne, które tak naprawdę okazuje się szkodliwe dla komputera. Aby uniknąć takich wpadek warto jest w celu pobrania takiego oprogramowania korzystać z zaufanych stron. Szczególną uwagę należy zwrócić na darmowe antywirusy. Bądźmy więc w tej kwestii bardzo ostrożni i korzystajmy tylko ze sprawdzonych i znanych programów.

13 zasad bezpiecznego Internetu

Bezpieczeństwo w sieci to sprawa niezwykle istotna. Często słyszymy o namierzaniu kogoś przez Internet, o zawieraniu tragicznie kończących się znajomości albo utracie dużej ilości pieniędzy na produkty, które nigdy nie dotarły do klienta. Najczęściej jednak chodzi o zabezpieczenie przed wszelkiego rodzaju złośliwym oprogramowaniem oraz podłączającymi się pod różne pliki niechcianymi linijkami kodu. Tutaj znajdziecie wyliczone zasady, które pozwolą wam ograniczyć te zagrożenia i bezpiecznie korzystać z komputera oraz surfować po ulubionych stronach w sieci.

1. Pamiętaj o uruchomieniu firewalla. Najlepiej na poziomie średniej ochrony z możliwością ustalania reguł. Jeżeli nie jesteś pewien czy sobie poradzisz z ręczną obsługą reguł, możesz ustawić średni lub nawet wysoki poziom ochrony.

2. Zainstaluj i używaj oprogramowania przeciw wirusom i spyware. Najlepiej stosuj ochronę w czasie rzeczywistym.

3. Aktualizuj – oprogramowanie oraz bazy danych wirusów (dowiedz się czy twój program do ochrony przed wirusami posiada taką funkcję i robi to automatycznie, często nie mają jej programy darmowe).

4. Nie otwieraj plików nieznanego pochodzenia.

5. Nie korzystaj ze stron banków, poczty elektronicznej czy portali społecznościowych, które nie mają ważnego certyfikatu, chyba, że masz stuprocentową pewność z innego źródła, że strona taka jest bezpieczna.

6. Nie używaj niesprawdzonych programów zabezpieczających czy też do publikowania własnych plików w Internecie (mogą one np. podłączać niechciane linijki kodu do źródła strony).

7. Co jakiś czas skanuj komputer i sprawdzaj procesy sieciowe – jeśli się na tym nie znasz poproś o sprawdzenie kogoś, kto się zna. Czasami złośliwe oprogramowanie nawiązujące własne połączenia z Internetem, wysyłające twoje hasła i inne prywatne dane do sieci może się zainstalować na komputerze mimo dobrej ochrony – należy je wykryć i zlikwidować.

8. Sprawdzaj pliki pobrane z Internetu za pomocą skanera, nawet jeśli wydają się niezarażone (ostrożności nigdy za wiele).

9. Staraj się nie odwiedzać zbyt często stron, które oferują niesamowite atrakcje (pieniądze, darmowe filmiki, muzykę, albo łatwy zarobek przy rozsyłaniu spamu) – często na takich stronach znajdują się ukryte wirusy, trojany i inne zagrożenia.

10. Jeżeli musisz wejść na stronę niepewną z punktu widzenia bezpieczeństwa korzystaj z pośrednictwa bramek PROXY.

11. Ważna zasada dotycząca bezpieczeństwa osobistego: nie zostawiaj danych osobowych w niesprawdzonych serwisach i na stronach, jeżeli nie masz absolutnej pewności, że nie są one widoczne dla osób trzecich.

12. Nie wysyłaj w e-mailach żadnych poufnych danych.

13. Pamiętaj, że żaden bank nie wysyła e-maili do swoich klientów z prośbą o podanie hasła lub loginu w celu ich weryfikacji.

Do tych zasad można jeszcze dodać jedną, niekoniecznie związaną bezpośrednio z Internetem. Jeżeli chcesz być pewien bezpieczeństwa swojego komputera nie podłączaj do niego dysków przenośnych ani kart pamięci nieznanego pochodzenia, nie podłączaj też swojego dysku do nieznanego komputera.

źródło: www.bezpiecznypc.pl

Stream API in Java 8

Filed in KomputerowoTags: , , ,

Strumień

Strumień (nie należy mylić tego pojęcia ze strumieniami wejścia-wyjścia)
stanowi sekwencję elementów, ale nie przechowuje tych elementów w strukturach danych, tylko konstytuuje potok (połączenie) różnych operacji na elementach. Potok operacji ma swoje źródło (można to nazwać też źródłem strumienia), który może być np. kolekcją, tablicą lub napisem (ciągiem znaków).

Operacje, łączone w potoku, mogą być:

  • pośrednie – dające w wyniku inny strumień (np. map, filter), przy czym operacje te mogą być
    – bezstanowe – niewymagające od strumienia znajomości wartości poprzednio przetwarzanych elementów (np. filter lub forEach); takie operacje są bardzo efektywne;
    – stanowe – wymagające od strumienia pamiętania stanów innych, od akurat przetwarzanego, elementów (np. sortowanie elementów strumienia);
  • terminalne – kończące przetwarzanie strumienia (np. collect i forEach);
    po wykonaniu operacji terminalnej nie można wykonać na strumieniu żadnej innej operacji;
  • skracające (short-circuit) – takie, które powodują, że wcześniejsze w potoku operacje pośrednie kończą działanie, gdy rezultat operacji skracającej jest jasny; operacje te mogą być zarówno pośrednie, jak i terminalne.

Strumienie charakteryzują się ważnymi cechami.

  • Operacje pośrednie są leniwe (lazy operations), czyli nie są wykonywane ani nie generują żadnych wartości, dopóki nie zostanie wywołana operacja terminalna, a elementy strumienia są przetwarzane tylko w takim zakresie, jaki jest potrzebny do uzyskania wymaganego wyniku (np. gdy rezultat operacji skracającej jest w pełni określony).
  • Elementy strumienia mogą być generowane ad hoc za pomocągeneratorów i iteratorów (ogólnie takie strumienie są nieskończone; w praktyce zawsze jakoś kończymy ich przetwarzanie, np. generator liczb pseudolosowych może generować nieskończoną ich sekwencję, a my ograniczamy ją do pierwszych 100).
  • Operacje na strumieniach mogą być wykonywane równolegle (w odrębnych wątkach), przy czym bardzo łatwo to możemy zadekretować (np. za pomocą metody parallel), a podziałem pracy między równolegle wykonujące się podzadania zajmie się JVM.
  • We wszystkich operacjach strumieniowych podajemy lambda-wyrażenia, co sprzyja zwięzłości i czytelności kodu.

Strumienie są obiektami klasy implementującej interfejs Stream. Strumienie możemy uzyskać m.in.:

  • od kolekcji – za pomocą metody stream() z interfejsu Collection, np. list.stream();
  • od tablic-za pomocą statycznej metody stream() z klasy Arrays, np. Arrays.stream(array);
  • od napisów (strumień znaków) – za pomocą metody chars();
  • od podanych wartości, w tym tablic obiektowych (varargs), za pomocą metody
  • Stream.of(zestaw wartości);
  • od plików (strumień wierszy pliku) – za pomocą statycznej metody lines(…) z klasy Files lub lines() z klasy BuferredReader;
  • od katalogów (strumień reprezentujący drzewo katalogowe obiektów plikowych) – za pomocą statycznej metody walk(…) lub find(…) z klasy Files;
  • od archiwów (np. ZipFile lub JarFile; tu strumień reprezentuje uporządkowane elementy archiwum entries) za pomocą metody stream();
  • od wyrażenia regularnego, reprezentowanego przez obiekt klasy Pattern – za pomocą metody splitAsStream(napis), zwracającej strumień symboli napisu, wyłuskanych za pomocą tego wyrażenia;
  • za pomocą generowania elementów metodami Stream.generate(…) lub Stream.iterate(…);
  • za pomocą statycznej metody ints() klasy Random (strumień pseudolosowych liczb całkowitych;
  • przez leniwe połączenie strumieni statyczną metodą Stream.concat(strum1, strum2).

Podstawowe operacje pośrednie:

  • map(Function f) – Zwraca strumień z elementami tego strumienia przekształconymi za pomocą podanej funkcji (transformacja elementów).
  • flatMap(Function f) – Zwraca strumień z elementami tego strumienia przekształconymi za pomocą podanej funkcji, przy czym zastosowanie tej funkcji musi dawać strumienie, a po odwzorowaniu ich elementy będą stanowić jeden strumień (nastąpi swoiste spłaszczenie, połączenie różnych zestawów danych w jeden).
  • filter(Predicate p) – Zwraca strumień elementów, dla których predykat p daje true (selekcja elementów).
  • distinct() – Zwraca strumień elementów, różniących się w sensie equals() (wybór niepowtarzających się elementów) – operacja stanowa.
  • sorted(…) – Zwraca strumień posortowanych elementów (w porządku naturalnym lub wg podanego komparatora) – operacja stanowa.
  • unordered() – Zwraca nieuporządkowany strumień.
  • limit(int n) – Zwraca strumień zawierający n pierwszych elementów tego strumienia – operacja skracająca.
  • generate(Supplier s) – Zwraca strumień elementów, z których każdy jest tworzony przez podanego „dostawcę”, np. strumień stałych lub losowych wartości.
  • iterate(T init, UnaryOperator op) – Zwraca strumień tworzony przez iteracyjne zastosowanie operatora op wobec inicjalnej wartości init.
  • substream(…) – Zwraca część strumienia (np. od 10 elementu do końca lub od 5 do 20) – operacja skracająca.
  • parallel () – Zwraca strumień, na którym operacje będą wykonywane równolegle.
  • sequential() – Zwraca strumień, na którym operacje będą wykonywane sekwencyjnie

Wybrane operacje terminalne:

●allMatch(Predicate p) – Zwraca true, jeśli wszystkie elementy strumienia spełniają warunek p, false w przeciwnym razie.
●anyMatch(Predicate p) – J.w., ale pytanie brzmi: czy jakikolwiek element strumienia spełnia warunek; jest to operacja skracająca.
●noneMatch(Predicate p) – J.w, czy żaden element strumienia nie spełnia podanego warunku – operacja skracająca.
●findAny() – Zwraca dowolny element strumienia jako obiekt typu Optional, zawierający wartość tego elementu lub wartość pustą, jeśli strumień jest pusty – operacja skracająca.
●findFirst() – Zwraca pierwszy element strumienia (jako Optional z jego wartością lub wartością pustą, gdy strumień jest pusty) – operacja skracająca.
●long count() – Zwraca liczbę elementów strumienia.
●void forEach(Consumer c) – Wykonuje podane działanie na każdym elemencie strumienia; ale kolejność przetwarzania nie musi być taka sama, jak kolejność w zestawie, na który nałożono strumień (np. elementy listy mają swoją kolejność, ale forEach na strumieniu związanym z listą może wykonywać działania w innej kolejności).
●forEachOrdered(Consumer) – j.w., ale jeśli dane, od których uzyskano strumień, mają swoją kolejność, to przy przetwarzaniu zostanie ona zachowana.
●reduce(…) – Wykonuje redukcję elementów strumienia, czyli ogólnie – na podstawie zestawu jego elementów, stosując wobec nich odpowiednie operacje akumulujące czy kombinujące, wytwarza pewną wynikową wartość (np. sumę).
●collect(…) – Wykonuje tzw. redukcję modyfikowalną, akumulującą elementy strumienia do modyfikowalnego kontenera, takiego jak kolekcja, mapa, bufor znakowy, inny strumień.
●max(…) i min(…) – Zwracają największy (najmniejszy) element strumienia w porządku określonym przez podany jako argument komparator.
●toArray(…) – Zwraca tablicę elementów strumienia.

Przetwarzanie strumieniowe

Sekwencja działań jest następująca.
Od listy metodą stream() uzyskujemy tzw. strumień (inaczej zwany sekwencją), a na danych strumienia możemy wykonywać m.in. operacje przetwarzania (metoda map), filtrowania (metoda filter) oraz uzyskiwać nowe kolekcje wyników tych operacji (metoda collect). Metodzie map podajemy lambda-wyrażenie, którego wynikiem jest przetworzenie jego parametru (działa to tak jak transform() z naszego interfejsu Transformer). Metoda zwraca strumień, na którym można wykonywać dalsze operacje. Metodzie filter podajemy lambdę przekształcającą parametr w wartość boolowską (tak jak nasz filter()). Filtrowanie zwraca strumień, który pozwala wykonywać ew. dalsze operacje tylko na tych danych, dla których wynik lambdy jest true. Możemy zatem wywoływać sekwencje map-filter, realizując kolejne etapy przetwarzania strumienia danych. Metoda collect natomiast kończy przetwarzanie strumienia i ma za parametr obiekt klasy Collector, który tworzy nową kolekcję i dodaje do niej dane ze strumienia, na rzecz którego została wywołana. W szczególności taki kolektor, budujący listę, można uzyskać za pomocą statycznej metody Collectors.toList().

Możemy zatem napisać przykład uzyskania listy kwadratów tych elementów listy src, które są mniejsze od 10:

import static java.util.stream.Collectors.toList;
// …
List<Integer> src = Arrays.asList(5, 72, 10, 11, 9);
List<Integer> target = src.stream().filter(n -> n < 10).map(n -> n * n).collect(toList());

Może trochę więcej tu kodu, niż tradycyjnie, ale za to nie musimy definiować własnych interfejsów (metody strumieniowe korzystają z interfejsów z pakietu java.util.function, które pokrywają częste przypadki użycia lambda-wyrażeń). Ale co najważniejsze mamy dużo większą swobodę działania. Możemy np. filtrować kwadraty liczb, czyli najpierw wykonać map(), a późniejfilter():

List<Integer> num = Arrays.asList(1, 3, 5, 10, 9, 12, 7);
List<Integer> out = num.stream().map(n -> n*n).filter(n -> n > 80).collect(toList());

co da w wyniku listę liczb: [100, 81, 144]

W klasie Stream nie ma bezpośredniej metody konwertującej strumień do listy jak toList() (jest konwersja do tablicy: toArray ()). Do konwersji na kolekcję strumienia stworzono metodę collect() i klasę Collector. Podobne wyniki można uzyskać także w inny sposób.

  • streamOfString.collect(Collectors.toList()); //typ listy pozostaje ten sam, to może nie wystarczyć po przetworzeniu strumienia
  • streamOfString.collect(Collectors.toCollection(ArrayList::new)); //tworzymy nową listę
  • ArrayList list = new ArrayList<>(); streamOfLetters.forEach(list::add); //dodajemy elementy do istniejącej listy
  • ArrayList myList = new ArrayList<>(); streamOfNumbers.parallel().forEachOrdered(myList::add); //ze względu na równoległość strumienia można stracić kolejność, forEachOrdered nakazuje utrzymywać kolejność elementów na liście jaka była w strumieniu.

Przykład z klasą opisującą państwa.
Załóżmy, że mamy plik z informacjami o krajach (zaczerpnięty z serwisu GeoNames,
gdzie wymienione są nie tylko niepodległe państwa, ale również regiony, które mają
odrębne kody ISO2; w informacjach tych nie ma gęstości zaludnienia, a kontynenty są
dane tylko w postaci kodów). Na wiersze tego pliku możemy nałożyć strumień i łatwo je
przetwarzać na różne sposoby.
Utwórzmy najpierw listę obiektów klasy Country. Zastosujemy metody map i collect strumieni do tworzenia listy obiektów klasy na podstawie wierszy pliku:

Path p = Paths.get(„nazwa_pliku”);
Stream<String> ls = Files.lines(p, Charset.defaultCharset());
List<Country> clist = ls.map(Country::new).collect(Collectors.toList());

Tutaj map(Country::new) tworzy strumień obiektów klasy Country (na wierszach pliku jest wywoływany jej konstruktor, który inicjuje pola na podstawie informacji ze stringa), a metoda collect posługuje się predefiniowanym obiektem-kolektorem (statyczne odwołanie do klasy Collectors), który dodaje elementy strumienia do nowo utworzonej listy. Klasa Collectors dostarcza wielu innych gotowych kolektorów do wykorzystania w metodzie collect (np. do tworzenia różnych rodzajów kolekcji, a także grupowania elementów strumienia pod kluczami w mapach).
Od utworzonej listy krajów możemy pobrać strumień i wykonywać na nim inne operacje, np. przez clist.stream().count() dowiemy się, że mamy informacje o 250 krajach. Spróbujmy teraz odnaleźć pierwszy kraj w strumieniu, którego nazwa zaczyna się na „B” i pobrać jego nazwę.

Optional<String> first = clist.stream()
.map(Country::getName) // strumień wszystkich nazw
.filter(s -> s.startsWith(„B”)) //strumień nazw na B
.findFirst(); // pierwsza nazwa na B
String nazwa = first.get();
System.out.println(nazwa);

Jako wynik findFirst() mamy Optional, bo w strumieniu może nie być żadnego elementu. Optional opakowuje uzyskaną wartość, musimy ją jeszcze pobrać za pomocą metody get(). Jednak jeśli jej nie ma (np. w filter wyszukujemy kraje o nazwie zaczynającej się na X), to wywołanie get()spowoduje wyjątek. Aby tego uniknąć, powinniśmy zastosować następująca konstrukcję:
Optional<String> first … String nazwa = first.orElse(…) której rezultatem będzie wynik get() (jeśli jest), albo podany przez nas argument metody orElse. Dobrze to widać w następującym fragmencie:

static String firstWithPrefix(Stream<Country> str, String p){
return str.map(Country::getName)
.filter(s -> s.startsWith(p))
.findFirst().orElse(„nie ma kraju na ” + p);
}
// …
List<Country> clist = …
String nazwa = firstWithPrefix(clist.stream(), „B”);
System.out.println(nazwa);
nazwa = firstWithPrefix(clist.stream(), „X”);
System.out.println(nazwa);

Wynik:
Bośnia and Herzegovina
nie ma kraju na X

główny materiał: http://pracownik.kul.pl/files/10382/public/W13.pdf

Interfejsy funkcyjne, lambda-wyrażenia

Filed in KomputerowoTags: , , , , ,

Programowanie funkcyjne – wprowadzenie

Języki programowania takie jak C/C++/Java/Python są nazywane imperatywnymi językami programowania, ponieważ zawierają sekwencje zadań do wykonania. Programista jawnie, krok po kroku definiuje jak wykonać zadanie. Programowanie funkcyjne działa inaczej. Zamiast sekwencyjnie wykonywać zadania, języki funkcyjne wyznaczają jedynie wartości poszczególnych wyrażeń.

Programy funkcyjne składają się jedynie z funkcji. Główny program jest funkcją, której podajemy argumenty, a w zamian otrzymujemy wyznaczoną wartość – wynik działania programu. Główna funkcja składa się tylko i wyłącznie z innych funkcji, które z kolei składają się z jeszcze innych funkcji. Każda operacja wykonywana podczas działania funkcji, a nie mająca związku z wartością zwracaną przez funkcję to efekt uboczny (np. operacje wejścia wyjścia, modyfikowanie zmiennych globalnych). Funkcje które nie posiadają efektów ubocznych nazywane są funkcjami czystymi (pure function).

Ideą programowania funkcyjnego jest pisanie programów w kategoriach „co ma być osiągnięte”, a nie – jak w programowaniu imperatywnym – poprzez specyfikowanie kolejnych kroków algorytmu do wykonania. Takie podejście jest możliwe, gdy w języku programowania możemy traktować fragmenty kodu (funkcje) jako pełnoprawne obiekty, które mogą być przekazywane innym funkcjom i zwracane z innych funkcji. W czystych językach funkcyjnych tak właśnie się dzieje, co więcej – funkcje nie zmieniają  żadnych danych (stanów), ważne jest jedynie wyliczanie ich wyników na podstawie podanych argumentów (co ma duże znaczenie np. przy przetwarzaniu równoległym).

Domknięcia(closure) – obiekt wiążący funkcję ze środowiskiem, w którym ona działa.
Zadaniem środowiska jest przechowywanie danych, nie mających charakteru globalnego, wykorzystywanych przez funkcję.
Domknięcia są nazywane także wyrażeniami lambda (lambda expressions).

W Javie w wersji 8 wprowadzono (w ograniczonym zakresie) wsparcie dla programowania funkcyjnego. Obejmuje ono przede wszystkim lambda-wyrażenia oraz przetwarzanie strumieniowe.
O lambda-wyrażenia możemy myśleć jako o bezpośrednio podawanych fragmentach kodu, swoistych funkcjach bez nazwy, które są traktowane „jak prawdziwe obiekty”, czyli np. mogą być przekazywane metodom.

W elastycznie wykorzystywanej metodzie (dla różnych typów danych, różnych warunków, różnych operacji) dzięki lambda-wyrażeniom sformułowanie tego, co chcemy osiągnąć, jest bardzo proste i czytelne.

Dla większej uniwersalności metod (być może chcielibyśmy najpierw przetworzyć dane, a filtrować wyniki tego przetwarzania. a może filtrować, przetwarzać i znowu filtrować?) często zastępujące i znacznie poszerzające ich funkcjonalność przydatne jest przetwarzanie strumieniowe.

Interfejsy funkcyjne i lambda-wyrażenia

Jeżeli zestaw metod, deklarowanych w interfejsie, zawiera tylko jedną metodę abstrakcyjną (wyłączając dodane deklaracje abstrakcyjnych publicznych niefinalnych metod z klasy Object), to taki interfejs nazywa się funkcyjnym (SAM – Single Abstract Method). Przykłady interfejsów funkcyjnych:

Lambda-wyrażenia można stosować jako uproszczenie w użyciu anonimowych klas wewnętrznych, implementujących interfejsy funkcyjne. Podobnie jak w lokalnych klasach wewnętrznych lambda-wyrażenie ma dostęp:

  • do wszystkich składowych klasy otaczającej, przy czym pola tej klasy mogą być przez lambda-wyrażenie modyfikowane;
  • do zmiennych lokalnych, ale muszą one albo być deklarowane ze specyfikatorem final albo być efektywnie finalne (effective final).

Inaczej niż w anonimowych klasach wewnętrznych lambda-wyrażenie nie wprowadza nowego zasięgu widoczności i dostępności, co oznacza m.in., że

  • zmienna this jest referencją do obiektu klasy, w której tworzone jest lambda-wyrażenie (w klasie anonimowej this oznacza obiekt tej klasy);
  • słowo kluczowe super oznacza nadklasę obiektu (klasy), w której tworzone jest lambda-wyrażenie;
  • jeśli lambda-wyrażenie występuje w bloku, niedopuszczalne jest przesłanianie zmiennych lokalnych z bloku otaczającego (w klasie lokalnej anonimowej mamy nowy zasięg widoczności, więc możemy używać tych samych identyfikatorów zmiennych, co w bloku otaczającym).

Referencje do metod i konstruktorów

Często definicja lambda-wyrażenia sprowadza się do wywołania metody istniejącej klasy lub utworzenia jej obiektu. Wtedy zamiast wywołania tej metody w lambda-wyrażeniu możemy zastosować referencję do metody lub konstruktora. Są cztery rodzaje takich referencji (symbolicznie przez x oznaczamy jeden parametr lub listę parametrów w nawiasach okrągłych):
NazwaKlasy::nazwa_metody_statycznej
Klasa::metStat – odpowiada lambda-wyrażeniu
x -> Klasa.metStat(x)
● NazwaKlasy::nazwa_metody_niestatycznej
Klasa::met – odpowiada lambda-wyrażeniu
x -> x.met()
● zmienna_niestatyczna::nazwa_metody_niestatycznej
Klasa v;
v::met – odpowiada lambda-wyrażeniu
x -> v.met(x)
● NazwaKlasy::new
Klasa::new – odpowiada lambda-wyrażeniu
x -> new Klasa(x)

Naturalnie, liczba i typy parametrów oraz typ wyniku metody (konstruktora), do których używamy referencji, muszą odpowiadać deskryptorowi funkcji jakiegoś interfejsu funkcyjnego (który zostanie dobrany do realizacji lambda-wyrażenia).

przykład str.24-26 http://pracownik.kul.pl/files/10382/public/W13.pdf

Gotowe interfejsy funkcyjne

Najczęściej nie musimy tworzyć własnych interfejsów funkcyjnych.
W Javie są już  interfejsy typu SAM (np. FileFilter czy Runnable). Dodatkowo w wersji 8 pojawił się pakiet java.util.function, w którym zdefiniowano wiele interfejsów funkcyjnych dla częstych przypadków użycia lambda-wyrażeń, np.
Predicate<T>, Function<S, T>, UnaryOperator<T>, Supplier<T>, Consumer<T>(), BiPredicate<U,V>, BiFunction<U,V,R>, BinaryOperator<T>, BiConsumer<U,V>.

Interfejsy z których już korzystaliśmy:
A) ActionListener – ma jedną metodę:
void actionPerformed(ActionEvent ae)

B)FileFilter – ma jedną metodę:
boolean accept(File f)

wykorzystywaną do selekcji obiektów plikowych np. w trakcie listowania zawartości katalogu metodą listFiles (tu jako argument podajemy właśnie implementację metody accept). Zatem dla uzyskania tablicy plików z danego katalogu dir, mających podane rozszerzenie ext i czas modyfikacji późniejszy od podanego time możemy napisać:

File[] files = new File(dir).listFiles(f -> f.isFile() && f.getName().endsWith(„.”+ext)&& f.lastModified() > time);

C) Comparator – ma jedną metodę:
int compare(T obiekt1, T obiekt2)

Przy porównywaniu i sortowaniu możemy więc używać wygodnych lambda-wyrażeń, np. do uporządkowania listy napisów wg ich długości:

List<String> lst = Arrays.asList(„ala”, „ma”, „kota”, „i”, „pieska”);
Collections.sort(lst, (s1, s2) -> s2.length() – s1.length());
System.out.println(lst);
Powyższy fragment wyprowadzi: [pieska, kota, ala, ma, i]

D)Runnable

Kod zliczający czas (np. w trakcie jakiejś interakcji użytkownika z programem) za pomocą lambda-wyrażenia, pasującego do deskryptora funkcji (public void run()) moglibyśmy napisać tak:
Future<?> ftask = Executors.newSingleThreadExecutor().submit( () -> { int sec = 0; while(true) { sec++; try { Thread.sleep(1000); } catch(InterruptedException exc) { return; } System.out.println(sec/60 + „:” + sec%60);}});
// …
JOptionPane.showMessageDialog(null, „Close dialog to stop”);
ftask.cancel(true);

E) Callable – ma jedną metodę
V call() throws Exception

Lambda-wyrażenia w gotowych metodach

… (str.30-36) … http://pracownik.kul.pl/files/10382/public/W13.pdf

 

INTERFEJSY FUNKCYJNE

@FunctionalInterface – jest opcjonalne
Każdy interfejs posiadający dokładnie jedną metodę abstrakcyjną jest interfejsem funkcyjnym, a zatem interfejsami funkcyjnymi są m.in.:

  • java.lang.Runnable
  • java.awt.event.ActionListener
  • java.lang.Comparable<T>

JAVA.UTIL.FUNCTION:

Słowa kluczowe:
Consumer – przyjmuje, nie zwraca (konsument)
Function – przyjmuje i zwraca (funkcja, działanie)
Predicate – przyjmuje i zwraca boolean (predykat, test logiczny)
Supplier – nie przyjmuje, daje (dostawca)
Operator – przyjmuje i zwraca ten sam typ (operator, obsługiwacz)
Unary, Binary (Bi) – jednoargumentowy, dwuargumentowy
Double, Int, Long, Boolean, Obj – typy

Interface (@FunctionalInterface) Description Przykład
Consumer<T>
void accept(T t);
Reprezentuje jednoargumentowego konsumenta (odbiorcę) – działanie, które przyjmuje jeden argument i nie zwraca wyniku. (inaczej: wykonuje bezwynikową operację na wartości typu T)
T->void
metoda: accept(Object)
np.: formatujemy tekst na małe literki + .txt
Consumer<String> formatuj=text->System.out.println(text.toLowerCase()+”.txt”);
formatuj.accept(„pLiK”);
//wynik: plik.txt
DoubleConsumer
IntConsumer
LongConsumer
przyjmuje 1 argument typu double (int, long) i nie zwraca wyniku double->void, int->void, long->void
metoda: accept(double), accept(int), accept(long)np.: formatujemy liczbę int przy wydruku

IntConsumer formatuj=liczba->System.out.println(liczba+” zł”);formatuj.accept(500); //wynik: 500 zł
BiConsumer<T,U>
Reprezentuje funkcję, które przyjmuje dwa argumenty i nie zwraca wyniku.
(T,U)->void
metoda: accept(Object, Object)
Function<T,R>
{ R apply(T t); }
Reprezentuje funkcję, która przyjmuje jeden argument i zwraca wynik (dokonuje przekształcenia wartości typu T na wartość typu R).
T->R
metoda: apply(Object)
np.: odwrotność liczby typu long:
Function<Long, Double> f = x ->1.0/x
System.out.println(f.apply(4L)); //wynik: 0,25
DoubleFunction<R>
IntFunction<R>
LongFunction<R>
Reprezentuje funkcję, która przyjmuje jeden argument typu double i zwraca wynik.
double->R, int->R, long->R
metoda: apply(double), apply(int), apply(long)
BiFunction<T,U,R>
R apply(T t, U u);
Reprezentuje funkcję, która przyjmuje dwa argumenty różnych typów i daje wynik innego typu.
(T,U) -> R
metoda: apply(Object, Object)

np.: sklejamy tekst spacją:

BiFunction <String, String, String> f=(x,y) -> x+” „+y;
System.out.print(f.apply(„A”, „LA”) //wynik: ALA
Predicate<T>
boolean test(T t);
Reprezentuje predykat* unarny.
Test logiczny  dla jednego argumentu (dla wartości typu T)
T->boolean
metoda: test(Object)
np.: sprawdzamy występowanie znaku w stringu
Predicate <String> f=x->x.contains(„u”);
System.out.println(f.test(„Kura”)); //wynik: true
DoublePredicate
IntPredicate
LongPredicate
Test logiczny dla jednego argumentu typu: double (int, long).  double->boolean, int->boolean, long->boolean
metoda: test(double), test(int), test(long),
BiPredicate<T,U>
Reprezentuje predykat* (wartość funkcji logicznej)  dwuargumentowy (podanych dwóch typów).
(T,U)->boolean
metoda: test(Object, Object)

np.: testujemy czy 1 argument jest mniejszy od 2

BiPredicate<Integer, Integer> f=(x,y) -> x<y;
System.out.print(f.test(2, 5); //wynik: true
Supplier<T>
T get();
Reprezentuje dostawcę wyników. Nie przyjmuje argumentów ale zwraca wynik (obiekt klasy T). (inaczej: dostarcza bezwarunkową wartość typu T)
( )->T
metoda: get( );
DoubleSupplier
IntSupplier
LongSupplier
Reprezentuje dostawcę wyników typu: double, int, long.
 ( )->double, ( )->int, ( )->long
BooleanSupplier
Reprezentuje dostawcę wartości wyników logicznych.
()->boolean
metoda: getAsBoolean( )
UnaryOperator<T>
Reprezentuje funkcję operującą na jednym argumencie, dając wynik tego samego typu.
T->T

metoda: Function.apply(Object)
np.: zamiana tekstu na wersaliki

UnaryOperator<String> f=s->s.toUpperCase();
System.out.println(f.apply(„Koc”)); //wynik: KOC
BinaryOperator<T>
extends BiFunction<T,T,T>
Reprezentuje funkcję działającą na dwóch argumentach tego samego typu i tworzącą wynik tego samego typu.
(T,T)->T

metoda: BiFunction.apply(Object, Object)
np.: wyliczamy iloczyn 2 liczb

BinaryOperator<Integer> f=(x,y) -> x*y;
System.out.print(f.apply(2, 5); //wynik: 10
DoubleBinaryOperator
IntBinaryOperator
LongBinaryOperator
Reprezentuje operację na dwóch argumentach typu: double, int, long i dającą wynik tego samego typu.
DoubleUnaryOperator
IntUnaryOperator
LongUnaryOperator
Reprezentuje funkcję na jednym argumencie typu: double, int, long i zwracającą wynik tego samego typu.
DoubleToIntFunction
DoubleToLongFunction
Reprezentuje funkcję przyjmującą jeden argument typu double i zwracającą wynik typu int (long).
double->int, double->long
metoda: applyAsInt(double), applyAsLong(double)
IntToDoubleFunction
IntToLongFunction
Reprezentuje funkcję przyjmującą jeden argument typu int i dającą wynik typu double (long). int->double, int->long
metoda: applyAsDouble(int), applyAsLong(int)
LongToDoubleFunction
LongToIntFunction
Reprezentuje funkcję przyjmującą jeden argument typu long i zwracającą wynik typu double (int). long->double, long->int
metoda: applyAsDouble(long), applyAsInt(long)
ToDoubleFunction<T>
ToIntFunction<T>
ToLongFunction<T>
Reprezentuje funkcję jednoargumentową, która daje w wyniku wynik określonego typu: double, int, long.
metoda: applyAsDouble(Object), applyAsInt(Object), applyAsLong(Object)
ToDoubleBiFunction<T,U>
ToIntBiFunction<T,U>
ToLongBiFunction<T,U>
Reprezentuje funkcję dwuargumentową, która daje w wyniku wynik określonego typu: double, int, long. metoda: applyAsDouble(Object, Object), applyAsInt(Object, Object), applyAsLong(Object, Object)
ObjDoubleConsumer<T>
ObjIntConsumer<T>
ObjLongConsumer<T>
Reprezentuje operację przyjmującą dwa argumenty typu: Object i double (Object i int, Object i long) i nie zwraca wyniku.
 metoda: accept(Object, long)

*Predykat (orzecznik) to funkcja orzekająca o spełnieniu jakiegoś warunku (sprawdza go i zwraca prawdę bądź fałsz). Predykaty mogą być jednoargumentowe (unarne), na przykład funkcja sprawdzająca, czy liczba jest parzysta, lub dwuargumentowe (binarne), przykładowo funkcja sprawdzająca, czy podane argumenty są sobie równe.

Materiały:

http://pracownik.kul.pl/files/10382/public/W13.pdf

https://docs.oracle.com/javase/8/docs/api/java/util/function/package-summary.html

http://torun.jug.pl/materials/meetings/1/Wprowadzenie_do_wybranych_zagadnien_JDK_8_by_Szymon_Stepniak.pdf

Interfejsy

Filed in KomputerowoTags: ,

Interfejsy zostały stworzone dla klas o podobnych właściwościach czy zachowaniach ale nie powiązanych ze sobą (dziedziczenie) posiadających za to pewne wspólne cechy (wymagane jest, by je posiadały). Każda klasa, która implementuje interfejs musi implementować wszystkie jego metody.

Klasy abstrakcyjne pozwalają na określenie czegoś w rodzaju zarysu klasy, który przyjmuje swoją ostateczną postać dopiero w klasie potomnej.
Struktura interfejsu przypomina strukturę klasy, ale nie zawiera zmiennych, które mogą zmieniać wartość (choć może zawierać stałe z przypisaną wartością) i zwykle nie posiada implementacji zadeklarowanych w niej metod.

O interfejsach należy myśleć jako o całkowicie abstrakcyjnych klasach. Interfejs może mieć metody, ale tylko abstrakcyjne, czyli nie może zawierać ich implementacji. Klasy abstrakcyjne mogą zawierać zarówno metody abstrakcyjne (bez implementacji), jak i zwykłe metody (z implementacją w klasie abstrakcyjnej).

Sens tworzenia interfejsów jest taki, że pozwalają one na opisanie tego jaką funkcjonalność powinny mieć klasy, ale nie jak ją mają realizować. Do podstawowych różnic w porównaniu do klas abstrakcyjnych i ogólniej mechanizmu dziedziczenia jest to, że o ile klasa pochodna może dziedziczyć bezpośrednio tylko po jednej klasie, to klasa może implementować na raz kilka interfejsów.

Właściwości interfejsów:

  • Wszystkie metody interfejsu są domyślnie publiczne i abstrakcyjne, nie trzeba tego zaznaczać przy deklaracji.
  • Wszystkie pola interfejsu są domyślnie publiczne, statyczne i finalne (też nie trzeba tego deklarować). Wszystkie klasy, które implementują interfejs, będą miały zawsze bezpośredni dostęp do tych samych (stałych) wartości.
  • Metody interfejsów nie mogą być statyczne (w javie 8 już mogą), nie mogą być oznaczone jako final, strictfp, native (finalnych metod nie można nadpisać w klasie implementującej interfejs). Od javy 8 pojawiają się metody domyślne (z modyfikatorem default), które pozwalają zdefiniować ciało metody w interfejsie. Co więcej, zachowujemy możliwość wielokrotnego dziedziczenia!
  • Interfejsy mogą rozszerzać jedynie inne interfejsy.
  • Interfejs nie może implementować innego interfejsu.
  • Deklaracja interfejsu odbywa się przy pomocy słowa kluczowego „interface”.

Implementacje metod z interfejsu, nie mogą deklarować nowych, ani modyfikować już zadeklarowanych wyjątków. W implementowanej metodzie nie jest wymagane, aby powtórnie deklarować wyjątek.

Np.:

public interface Zwierzak {
   String jedz( );
   Object rozmnażaj_Się( );
   void rośnij(double wzrostMasy);
}

Jak widać, są to same deklaracje określające nazwę metody, rodzaj wartości zwracanej przez metodę oraz argumenty, które przyjmuje.
Jeśli klasa implementuje interfejs to znaczy, że zgadza się na swoisty kontrakt, że zaimplementuje wszystkie metody w niej zawarte.

public class Mysz implements Zwierzak {
…tu muszą być stworzone (zaimplementowane) metody:
jedz( ), rozmnażaj_Się( ), rośnij( )
… inne elementy klasy, np. metoda jedz_ser( );
}

Teraz obiekt:
Zwierzak mysiok = new Mysz( );
jest typowym zwierzakiem (bo: je, rozmnaża się, rośnie) i jego opis i  zachowania jest typowe dla wszystkich zwierzaków (deklarowanych w interfejsie Zwierzak)
Mysz mysiok = new Mysz( );
jest myszą „pełną gębą” i prócz wspólnych cech zwierzaków potrafi jeszcze jeść_ser

jeśli teraz w głównym programie stworzymy metodę:
nakarmZwierzaka(Zwierzak z) {   z.jedz(); }

to przyjmuje ona jako argument każdy obiekt, który został utworzony na podstawie klasy implementującej interfejs Zwierzak.
Wykorzystuje metody, które są w tym interfejsie zadeklarowane i nie musi “wiedzieć” jakiego dokładnie typu są te obiekty, w których implementacja tych metod a także wynik ich działania może być zupełnie inny.

Jeśli mamy interfejs pracownik, w którym firma określiła kontrakt, że by zostać pracownikiem firmy trzeba opisać swoją metodę pracy w określony sposób podając w wyniku zapłatę to: jeśli młynarz chce być pracownikiem musi tę metodę opisać wg tego schematu. Przy pewności, że każdy rodzaj pracownika określił zapłatę (a braku zainteresowania w szczegółach jak pracuje) – możemy przy określonym budżecie i wymaganiach co do rodzaju pracownika zaplanować wykonanie jakiejś pracy.

Wraz z Javą w wersji 8 pojawiła się możliwość umieszczania w interfejsach także implementacji metod. W takich wypadkach są to tzw. “metody domyślne”. Metoda domyślna musi otrzymać modyfikator default.

Umieszczenie domyślnej implementacji nowych metod rozwiązuje problem wstecznej kompabitylności języka java, stare klasy w programach nie muszą ich implementować.
Drugą zaletą stosowania metod domyślnych jest to, że mogą one być tworzone wtedy, gdy przewidujemy, że implementujące interfejs metody mogą korzystać tylko z części funkcjonalności oferowanej przez interfejs. W takim przypadku programista nie jest zmuszony do implementowania metod, których i tak nie będzie wykorzystywać.

Jeśli teraz do interfejsu Zwierzak dopiszemy domyślną metodę:
default void kopNore() {
System.out.println(„Kopię…”);
}

to nie ma sensu jej implementować dla Słonia.

DZIEDZICZENIE METOD DOMYŚLNYCH
public interface A { default int foo(Integer n) { return n + n; } }
public interface B { default int foo(Integer n) { return n + 2;} }

public class C implements A,B {
@Override
public int foo(Integer n) {
return A.super.foo(n);
}
}

METODY STATYCZNE W INTERFEJSACH

public interface InterfaceWithStaticMethod {
public static String itWorks(int number) {
return String.format(„Works for %d”, number);
}
}
Zastosowanie: implementacja metod statycznych w interfejsie pozwoli zrezygnować z tzw. klas narzędziowych (przykład: java.util.Collections dla interfejsu java.util.Collection<E>

materiały:
http://ggoralski.pl