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:
Kanał ATOM
