poniedziałek, października 04, 2010

Go dla Java'owca ;-) odcinek 1 "klasy"

Od kilku dni czytam sobie o Go, głównie rekreacyjnie, ale z tyłu głowy mam to, że może kiedyś będzie trzeba przejść na Go, albo C/C++ (co chyba bardziej prawdopodobne) po tym jak Oracle zaczyna powoli rozrabiać z Java'ą ;-) [a wolałem żeby Suna kupił Oracle niż IBM... ;-)]

Pierwszym szokiem dla Java'owca będzie to, że choć Go jest językiem obiektowym [także!] to nie ma tam klas!

Ale nie ma obaw, spokojnie możemy dodawać metody do typów danych. Jedyny wymóg by typ danych był w tym samym pakiecie co metody.

Java'owy kod w stylu:
public class Point {
private int x, int y;
public Point(int x,int y) {
this.x = x;
this.y = y;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
public float distanceFrom00() {
return Math.sqrt(x*x+y*y);
}
}

W Go wygląda mniej więcej tak:
type Point struct {
x int;
y int;
}
func (p *Point) GetX() int {
return p.x
}
func (p *Point) GetY() int {
return p.y
}
func (p *Point) DistanceFrom00() float {
return float(math.Sqrt(float64(p.x*p.x)+float64(p.y*p.y)))
}

Czyli definiujemy nowy typ danych, a później metody dla tego typu.
Wołanie metod jest dokładnie takie samo jak w Java'ie czyli po kropce.
Ze złych wieści, tu znów są wskaźniki :-( Na szczęście język trochę pomaga, i np. w deklaracjach metod nie trzeba wcale robić (p *Point) można zrobić (p Point).
To p to receiver, który robi za odpowiednik this.
Stąd w getterach jest użyte p.x.

Dziedziczenie jest, choć ciut inaczej zorganizowane.
Jeżeli np. stworzymy nowy typ Point3, w którym dodamy jeszcze z, to mamy wtedy:
type Point3 struct {
Point
z int
}

Trzeba doimplementować jeszcze GetZ() i nadpisać DistanceFrom00(), co wygląda tak:
func (p *Point3) GetZ() int {
return p.z
}

func (p *Point3) DistanceFrom00() float {
return float(math.Sqrt(math.Pow(float64(p.Point.DistanceFrom00()),2)+float64(p.z*p.z)))
}


Gorzej jest z tworzeniem tych obiektów :-(
p:=Point{3,2} // tworzy strukturę z x=3 i y=2
p3:=Point3{Point{3,2},1} // tworzy Point3 z x=3,y=2,z=1.......
p4:=new(Point) // tworzy nowy Point, w p4 jest wskaźnik do niego z x=0, y=0

Jak widać w tym fragmencie jest to ohydne ;-) dlatego jak rozumiem zaleca się tworzenie metod fabrykujących, które będą zwracały nasze obiekty.

Teraz ważna rzecz, w Go nie ma modyfikatorów dostępu takich jak w Java'ie, nie ma tu public, private czy protected, jest jednak sposób na ograniczanie widoczności.
Jeżeli nazwa pola, typu, czy metody jest pisana dużą literą to takie coś będzie widoczne poza swoim pakietem, wszystko pisane z małej litery widoczne jest tylko w pakiecie.
To wydaje się mieć sens, w Java'ie ewidentnie programiści przynieśli zwyczaje z C++ i to co było pomyślane jako mechanizm namawiający do używania modyfikatorów dostępu tak by to pakiety były głównymi strukturami decydującymi o prywatności stało się mechanizmem do prywatyzowania na poziomie klas.

Jak było widać wyżej przez to jak się "dziedziczy", można dość łatwo wprowadzić wielodziedziczenie, z tym, że jak 2 "dziedziczone" typy mają pola czy metody o tej samej nazwie to się robią problemy i puki ich nie używamy to jest dobrze, ale jak użyjemy to jest błąd [nie przyglądałem się temu jeszcze więc wieżę na słowo temu co czytałem ;-)].

W Go jest za to coś takiego jak interfejsy :-) Ale działają one kompletnie inaczej niż w Java'ie, w każdym bądź razie w sferze deklarowania tego czy się je implementuje.....
Jeśli coś ma odpowiednie metody to implementuje dany interfejs i nie trzeba niczego deklarować!
Popatrzmy na kod:
type Printable interface {
String() string
}
type TosterInt struct { value int }
type TosterFloat struct { value float }
func (i TosterInt) String() string {
return fmt.Sprintf("%d",i.value)
}
func (f TosterFloat) String() string {
return fmt.Sprintf("%f",f.value)
}

I teraz wszędzie w kodzie możemy używać cosiów stworzonych z TosterInt i TosterFloat tak jakby były typu Printable :-) Czyli trzymać je np. w mapie gdzie wartości są Printable.
  m:=make(map[string]Printable)
m["TosterInt"]=TosterInt{3}
m["TosterFloat"]=TosterFloat{3.9}
for k,v:=range m {
fmt.Println(k," ",v)
}


Aha, tak biegiem, podstawowy program w Go wygląda tak
package main

import "fmt" // tutaj importujemy co nam potrzebne, zwykle potrzebujemy ;-)

func main() {
// nadziewka
var i = 1 // samo nam stworzy inta o wartości 1
var f float64 = 1.0 // samo nam stworzy float64 o wartości 1
i1:=1 // to samo co var i1=1
fmt.Println(i," ",f," ",i1)
}

W Go można przypisywać jednym := kilka wartości, funkcje też mogą zwracać kilka wartości np:
package main

import "fmt"

func toster() (string, int, bool) {
return "string",1,false
}


func main() {
a,b,c:=toster()
fmt.Println(a," ",b," ",c)
}

Wypisze "string 1 false".
To wieloprzypisywanie trochę na początku przeraża ;-)

Ok, to tyle, jak będę miał nastrój na dalszą naukę to napiszę więcej ;-)
Na razie zachęcam do przyjrzenia się tutorialowi [szczególnie prezentacji do której linki są na stronie tutoriala].

Nie jestem już tak obrzydzony do Go jak rok temu, ale nadal nie jest to mój ulubieniec ;-)
http://www.blogger.com/img/blank.gif
Podoba mi się to, że jak na razie wydaje się nie skupiać na "architekturze", nie muszę myśleć o hierarchii klas, nie muszę pilnować ciągle by wszystko co powinno dziedziczyło/implementowało to co powinno, to się tu samo wydaje robić. Potrzebuję metody to ją po prostu dopisuję......

Na razie jednak nie wychodzi mi komplikacja z kilku plików ;-) Używam portu dla Windows, o którym tu już kiedyś pisałem i niestety nie potrafi on chyba do końca pracować z wieloma plikami [jeżeli stworzy się swój własny pakiet], dodatkowo gdy program zrobi out of memory to pod Windows cały komputer zawisa, i po jakichś 30 sekundach program po prostu kończy swoje działanie, bez żadnej informacji........

Do prostych zabaw warto użyć umieszczonego na stronie Go placu zabaw ;-)


Podobne postybeta
Wysyłamy pliki do Google Docs przy pomocy Go :-)
Język Go dla Windows :-)
Go dla Java'owca ;-) odcinek 2 "kontenery dwa ;-)"
Go wolniejsze od C i JavaScript, i ciut szybsze niż Java ;-) [a jednak od Java'y też wolniejsze]
Wyważanie otwartych drzwi