Na przykładzie języków Java, C# i odrobiny C++ Dariusz Brzeziński Politechnika Poznańska, Instytut Informatyki
Refleksja Typy anonimowe Wyrażenia lambda Extension methods *LINQ
Java jest językiem dynamicznym Umożliwia wykorzystanie klas, których nazwy nie były znane podczas tworzenia i kompilacji programu String nazwaklasy = "test.osoba"; Object ob1 = null; try { Class c = Class.forName(nazwaKlasy); ob1 = c.newinstance(); catch (InstantiationException e) { System.out.println(e); catch (IllegalAccessException e) { System.out.println(e); catch (ClassNotFoundException e) { System.out.println(e);
Umożliwia odczyt informacji o klasach i obiektach (konstruktory, metody, pola) w trakcie pracy programu Przydatna dla dynamicznie ładowanych klas Nawet jeśli nie znamy nazw składowych możemy: Stworzyć instancję klasy Pobierać i ustawiać wartości pól obiektów Wywoływać metody Tworzyć i modyfikować tablice
Zwykłe programy nie potrzebują refleksji Programy, które przetwarzają programy potrzebują refleksji Typowe przyklady: Debugger Dynamicznie generowane GUI (np. Weka) IDE (np. Netbeans) Program dynamiczne ładujący pluginy
public static void main(string[] args) { try { Class cl = Class.forName("java.lang.String"); Constructor cnst[] = cl.getconstructors(); Field fld[] = cl.getdeclaredfields(); Method mtd[] = cl.getmethods(); System.out.println("Konstruktory klasy String:"); for (int i = 0; i < cnst.length; i++) { System.out.println(cnst[i].getName()); System.out.println("Pola:"); for (int i = 0; i < fld.length; i++) { System.out.println(fld[i].getName()); System.out.println("Metody:"); for (int i = 0; i < mtd.length; i++) { System.out.println(mtd[i].getName()); catch (ClassNotFoundException e) { e.printstacktrace();
static void Main(string[] args){ try { Type cl = Type.GetType("System.String"); ConstructorInfo[] cnst = cl.getconstructors(); FieldInfo[] fld = cl.getfields(); MethodInfo[] mtd = cl.getmethods(); Console.WriteLine("Konstruktory klasy String:"); for (int i = 0; i < cnst.length; i++) { Console.WriteLine(cnst[i].Name); Console.WriteLine("Pola:"); for (int i = 0; i < fld.length; i++) { Console.WriteLine(fld[i].Name); Console.WriteLine("Metody:"); for (int i = 0; i < mtd.length; i++) { Console.WriteLine(mtd[i].Name); catch (NullReferenceException e) { Console.WriteLine(e.StackTrace);
Dostępne jest praktycznie wszystko: Modyfikatory dostępu Parametry metod Pola Czy typ generyczny, zagnieżdżony, abstrakcyjny Paczka/Namespace Anotacje/Atrybuty
Typy anonimowe to klasy bez nazw Tworząc klasę anonimową od razu tworzy się jej wystąpienie Typy anonimowe nadają się do prostych pojedynczych zbiorników na dane, których potrzebujemy na chwilę
Object produkt = new Object(){ String nazwa = "Gizmo"; double cena = 239.99; String opis = "Rewelacyjny produkt"; ; double rzeczywista = 23; double urojona = 2.3; Object x = new Object (){ double r = rzeczywista; double u = urojona; ; Klient k = baza.pobierzklienta(23); Klient kontakt = new Klient(){ private Klient init(){.init(); imie = k.imie; email = k.email; return this;
var produkt = new { Nazwa = "Gizmo", Cena = 239.99, Opis = "Rewelacyjny produkt" ; var Rzeczywista = 23; var Urojona = 2.3; var x = new { Rzeczywista, Urojona ; Klient k = Baza.PobierzKlienta(23); var kontakt = new { k.imie, k.email ;
Ponieważ C# jest silnie typowanym językiem, należy podawać typ każdej zmiennej (tak jak w Javie) Można jednak pominąć nazwę typu, jeśli przypisana wartość jednoznacznie wyznacza typ Należy skorzystać ze słowa kluczowego var string pytanie = "A imię jego"; Int32 odpowiedz = 44; var pytanie = "A imię jego"; var odpowiedz = 44;
Od C++11 istnieje podobne słowo: auto Od C++14 słowo kluczowe auto może być umieszczone również jako nazwa zwracanego przez funkcję typu auto suma = 2 + 2; auto foo() { return 5;
Jest... Poniższy kod nie skompiluje się var pytanie = "A imię jego"; pytanie = 44; Ponadto typ musi być określony przy deklaracji var pozniejtozrobie; //błąd var amozenull = null; //nie działa var warunkowo; //to też nie if (warunek) warunkowo = 500; else warunkowo = 600; var warunkowo = warunek? 500 : 600; //ale to już tak!!!
Sposób na tworzenie anonimowych metod Wyrażenia lambda nadają się do operacji na chwilę Przykłady Java: x -> x + 1 (x, y) -> x * y () -> new Piwo() (x, y) -> { int result = x + y; return result; Przykłady C#: x => x + 1 (x, y) => x * y () => new Piwo() (x, y) => { var result = x + y; return result;
// Słowa, słowa, słowa List<String> words = new ArrayList<String>(); words.add("mój"); words.add("drogi"); words.add("leonato"); words.add("zgniłą"); // Słowa, słowa, słowa var words = new List<string> { "Mój", "Drogi", "Leonato", "zgniłą" ; // Sortujemy Collections.sort(words, (a, b) -> new Integer(a.length).compareTo(b.length)); // Wyniki for (String word : words) { if (word.startswith("d")){ System.out.println(word + " "); // Sortujemy words.sort((a, b) => a.length.compareto(b.length)); // Wyniki foreach (string word in words.where(w => w.startswith("d")) { Console.Write(word + " ");
Do standardu języka C++ również wprowadzono wyrażenia lambda, choć z trochę inną składnią [] (string& name) { cout << "Hello " << name; C++ pozwala również na przechwytywanie zmiennych widocznych z aktualnego zakresu string name; cin >> name; [&] () { cout << "Hello " << name;
Jeśli wyrażenie lambda wywołuje tylko istniejącą już metodę, można skorzystać z method reference Arrays.sort(publications, ); (a, b) -> Person.compareByAge(a, b) Arrays.sort(publications, Person::compareByAge);
W jakich jeszcze sytuacjach mogą być przydatne? Filtrowanie kolekcji Wyszukiwanie w kolekcjach Agregacja danych (sumy, średnie, itp.) Obsługa zdarzeń Przekazywanie prostych metod jako parametry
Od wersji 3.0 języka C# można roszerzać dowolną klasę (nawet zaimportowaną i zamkniętą) o metody Mechanizm ten polega na wywoływaniu metod statycznych za pomocą składni charakterystycznej dla metod niestatycznych W ten sposób można w wygodny sposób korzystać ze statycznych metod dla bardzo popularnych klas, których nie można zmieniać (np. String) Metody powstające dzięki temu mechanizmowi noszą nazwę Extension Methods
public static class IntEX { public static bool IsEven(this Int32 inttocheck) { return inttocheck % 2 == 0; public class HelloWorld { public static void Main(string[] args) { Console.WriteLine(2.IsEven()); Sic! Extension methods można wykorzystać do rozszerzenia interfejsu! Kto nie marzył o napisaniu implementacji choć jednej metody w interfejsie...
Klasa zawierająca extension methods musi być statyczna Pierwszym parametrem extension method jest zmienna rozszerzanego typu poprzedzona słowem kluczowym this Klasa zawierająca extension methods musi być zaimportowana (klauzula using) w klasie w której extension methods mają być wykorzystywane Metody rozszerzonej klasy mają pierwszeństwo nad extension methods
Od Javy 8 interfejsy mogą mieć metody domyślne Wprowadzone aby nie psuć wielu klas implementujących interfejsy zmienione po wprowadzeniu wyrażeń lambda Podczas kompilacji sprawdzane są konflikty przy dziedziczeniu wielu metod domyślnych o tej samej sygnaturze i wymuszane jest ich przesłonięcie Interfejsy wciąż nie mogą mieć pól ani konstruktorów, więc nie zrównały się z klasami abstrakcyjnymi
interface Person { default void sayhello() { System.out.println("Hello there!"); class Sam implements Person { class Main { public static void main(string [] args) { Sam sam = new Sam(); sam.sayhello();
LINQ to język zapytań będący rozszerzeniem języka C# Zapytania LINQ można wykonywać na: kolekcjach dziedziczących z IEnumerable plikach XML bazach danych Zapytania LINQ są równoważne ciągom wyrażeń lambda, z których większość to extension methods Do korzystania z LINQ oraz związanych z nim wyrażeń należy w kodzie dodać using System.Linq
public void Linq1() { int[] numbers = { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 ; var lownums = from n in numbers where n < 5 select n; public void Linq2() { string[] words = { "blueberry", "chimpanzee", "abacus", "banana", "apple", "cheese" ; var wordgroups = from w in words group w by w[0] into g select new { FirstLetter = g.key, Words = g ; foreach (var g in wordgroups) { Console.WriteLine("Words that start with the letter '{0':", g.firstletter); foreach (var w in g.words) Console.WriteLine(w);
public void Linq1() { int[] numbers = { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 ; var lownums = numbers.where(n => n < 5); public void Linq2() { string[] words = { "blueberry", "chimpanzee", "abacus", "banana", "apple", "cheese" ; var wordgroups = words.groupby(w => w[0]).select(r => new { FirstLetter = g.key, Words = g ); foreach (var g in wordgroups) { Console.WriteLine("Words that start with the letter '{0':", g.firstletter); foreach (var w in g.words) Console.WriteLine(w);