Autor wpisu: sokzzuka, dodany: 22.03.2011 22:38, tagi: javascript, php
Kolejny odcinek serii o języku Scala zajmie się kwestią traits – cech. Jest to o tyle ciekawa część języka, że w PHP również zostaną one wprowadzone w najbliższej wersji 5.4 . Traits w PHP zainspirowane zostały właśnie bezpośrednio implementacją z Scali.
Czym jest trait ? Trait jest elementem języka służącym do zapewnienia dziedziczenia poziomego. Co oznacza dziedziczenie poziome ?
Ażeby dowiedzieć się czym jest dziedziczenie poziome, najpierw musimy ustalić czym jest dziedziczenie pionowe. Dziedziczenie pionowe, takie jakie znamy np. z PHP i Javy zakłada, że każda klasa potomna może dziedziczyć tylko z jednej klasy bazowej. Wszelkie metody i pola oznaczone przez „protected
” obecne są też w klasie potomnej. Jednak co najważniejsze – klasa potomna jest podtypem klasy bazowej. Dziedziczenie oznacza, że klasa B jest również typu A .Ilustruje to poniższy przykład hierarchii klas znany z lekcji biologii:
Zwierzęta -> Strunowce -> Kręgowce -> Ssaki -> Walenie -> Delfiny -> Delfin Słodkowodny
Dziedziczenie poziome natomiast jest sytuacją gdy jakiejś klasie chcemy rozszerzyć funkcjonalność nie zaburzając wcześniej przedstawionej pionowej hierarchii klas. Weźmy dla przykładu naszego delfina i jego kolegę po miedzy – szympansa. Załóżmy, że chcemy zarówno delfina jak i szympansa nauczyć jakiś sztuczek np. salta w tył. Oczywiście moglibyśmy dodać metodę „salto w tył” abstrakcyjnej klasie „ssaki” z której dziedziczą szympans i delfin lub dodać kolejną klasę pośrednią. Natomiast pada pytanie jaki ma to sens ? W końcu tylko szympansy i delfiny umieją takie śmieszne sztuczki, natomiast inne ssaki nie są na tyle sprytne, żeby to wykonać. Generalnie wtłaczanie takiej cechy w dziedziczenie pionowe jest działaniem troszkę „na siłę”. Na szczęście mamy traity zwane też cechami. Traity w dowolnej ilości można „wczepić” do dowolnej klasy.
Traity często opisywane są jako interfejsy posiadające implementacje. Jest to dobre skojarzenie – implementacja przez jakąś klasę interfejsu oznacza, że posiada ona pewną funkcjonalność. Dołączenie traita do klasy znaczy dokładnie to samo, z tą różnicą, że trait może posiadać konkretną implementację.
Najwyższy czas pokazać, jak traity wykorzystywane są w praktyce w języku Scala. Trait, który przedstawię, będzie posiadał tylko jedną metodę – „toArray”. Będzie ona zwracała wszystkie zmienne istniejące w obiekcie w postaci obiektu klasy HashMap, który jest odpowiednikiem PHP-owego „arraya
„. Przy okazji dowiemy się co nieco o refleksji oraz o domknięciach leksykalnych i iteracji.
Kod:
trait ToArray { def toArray: HashMap[String, Any] = { val vars = new HashMap[String, Any]() val className = this.getClass.getName def extractFields(className: String):HashMap[String, Any] = { val currentClass = java.lang.Class.forName(className) val superClass = currentClass.getSuperclass if(superClass == null){ return vars } val fields = currentClass.getDeclaredFields fields.foreach((field:java.lang.reflect.Field) => { field.setAccessible(true) val name = field.getName val value = field.get(this) vars += name -> value field.setAccessible(false) }) return extractFields(superClass.getName) } extractFields(className) return vars } }
Metoda „toArray
” jest funkcją zwracającą obiekt typu HashMap
, gdzie klucz ma postać tekstową (String
), natomiast wartość może być dowolnego typu (Any
), jest ona również zadeklarowana jako stała – „vars
„.
By pobrać nazwę klasy, w której znajduje się trait, używamy metody this.getClass
, która zwraca nam Javowy obiekt reprezentujący klasę. Posiadający metodę, która zwraca nam jej nazwę „getName
„. Następnie zdefiniowana jest funkcja extractFields
, która jest jednocześnie domknięciem – importuje do swojego kontekstu stałą „vars
„. Rekurencyjnie przemieszcza się ona w górę hierarchii klas i pobiera listę zadeklarowanych pól i dołącza je do stałej vars. Pragnąłbym zwrócić tutaj szczególną uwagę na sposób w jaki jest dołączany kolejny element do „arraya
„. W PHP zrobilibyśmy coś takiego: