Figura[]
Transkrypt
Figura[]
Podstawy programowania obiektowego Technologie internetowe Wykład 7 Wydział Informatyki Politechniki Białostockiej, Cezary Bołdak Program wykładu ● Dziedziczenie ● Wywołania konstruktorów w dziedziczeniu ● Hermetyzacja w dziedziczeniu ● Dziedziczenie typu, rzutowanie w górę i w dół ● Mechanizm RTTI ● Nadpisywanie pól ● Nadpisywanie metod – polimorfizm ● Klasy i metody finalne ● Klasy i metody abstrakcyjne Wydział Informatyki Politechniki Białostockiej, Cezary Bołdak Dziedziczenie ● ● ● technika konstruowania nowych klas z wykorzystaniem klas już istniejących (jak kompozycja !) opisuje relację między 2 klasami: „... jest szególnym przypadkiem ...”, „... jest rodzajem ...” rzadziej stosowana i trudniejsza technika od kompozycji Komputer Figura Samochód Trójkąt Notebook Ciężarówka Wyścigówka Czworokąt Prostokąt Kwadrat Wydział Informatyki Politechniki Białostockiej, Cezary Bołdak Dziedziczenie w kodzie class Figura { ... } Klasa bazowa Nadklasa Klasa dziedziczona Base class Super class class Trojkat extends Figura { ... } class Czworokat extends Figura { ... } class Prostokat extends Czworokat { ... } Figura class Kwadrat extends Prostokat { ... } Klasa dziedzicząca Podklasa Derived class Base class Subclass Trójkąt Czworokąt Prostokąt Kwadrat Wydział Informatyki Politechniki Białostockiej, Cezary Bołdak Dziedziczenie jako zawieranie się podzbiorów ● każdy kwadrat jest prostokątem ● wśród prostokątów niektóre są szczególne: kwadraty ● kwadrat zatem dziedziczy z prostokąta FIGURY Figura CZWOROKĄTY TROJKĄTY PROSTOKĄTY Trójkąt Czworokąt KWADRATY Prostokąt Kwadrat Wydział Informatyki Politechniki Białostockiej, Cezary Bołdak Konsekwencje dziedziczenia ● klasa dziedzicząca przejmuje kod z klasy bazowej: class Figura { Punkt srodek; public void przesun(int dx, int dy) { srodek.przesun (dx, dy);} } class Kolo extends Figura { int r; } ... Kolo k = new Kolo(); k.srodek = new Punkt(5,5); k.r = 3; k.przesun(1,0); ● korzyści: ● mniej kodowania ● propagowanie zmian Wydział Informatyki Politechniki Białostockiej, Cezary Bołdak Konstruktory w dziedziczeniu ● Konstruktory nie dziedziczą się !!! class Figura { Punkt srodek; public Figura(Punkt s) { srodek = new Punkt(s); } } class Kolo extends Figura { int r; public Kolo(Punkt s, int rr) { srodek=new Punkt(s); r=rr; } } ... Figura f=new Figura( new Punkt(0,0)); Kolo k = new Kolo ( new Punkt(10,10), 5); ● ale to się nie skompiluje ● dlaczego ? Wydział Informatyki Politechniki Białostockiej, Cezary Bołdak → Obiekty klas dziedziczących ● tworzą się po kolei aż 4 obiekty (5) !!! Kwadrat k = new Kwadrat(new Punkt(1,1), 10)); 1 2 Figura środek new Figura(...) → 3 Czworokąt Figura Prostokąt Czworokąt Figura Kwadrat Prostokąt Czworokąt Figura środek środek środek new Czworokat(...) → new Prostokat(...) → new Kwadrat(...) ● a każdy obiekt to wywołanie konstruktora ● który potrzebuje parametrów ● który konstruktor ma się wywołać (przeciążanie) ? Wydział Informatyki Politechniki Białostockiej, Cezary Bołdak 4 Porządek wywołania konstruktorów ● ● każdy konstruktor klasy dziedziczącej musi wskazać konkretny konstruktor klasy bazowej, który wywoła się przed nim: ● nazwa klasy bazowej zastępowana jest słowem super ● konstruktor wybiera się podając jego parametry (przeciążanie!) ● wskazanie to musi być pierwszą instrukcją konstruktora jeśli tego nie zrobi kompilator wskaże konstruktor domyślny: class B extends A { public B() {} } ● → public B() { super(); } tu: zanim wywoła się domyślny konstruktor klasy B przed nim wywoła się domyślny konstruktor klasy A Wydział Informatyki Politechniki Białostockiej, Cezary Bołdak Przykład wywołań konstruktorów class A { public A() { System.out.println("A.A()"); } public A(int i) { System.out.println("A.A(int)"); } } class B extends A { public B() {super(0);System.out.println("B.B()"); } public B(int i) {System.out.println("B.B(int)"); } } class C extends B { public C() { System.out.println("C.C()"); } public C(int i) {super(i);System.out.println("C.C(int)");} } ... new C(); → A.A(int) B.B() C.C() new C(1); → A.A() B.B(int) C.C(int) Wydział Informatyki Politechniki Białostockiej, Cezary Bołdak A.A() A.A(int) B.B() B.B(int) C.C() C.C(int) Konstruktory jeszcze raz ● brakuje konstruktora domyślnego Figury class Figura { Punkt srodek; public Figura(Punkt s) { srodek = new Punkt(s); } } class Kolo extends Figura { int r; public Kolo(Punkt s, int rr) { super(); srodek=new Punkt(s); r=rr; } } to wstawia kompilator ... Figura f=new Figura( new Punkt(0,0)); Kolo k = new Kolo ( new Punkt(10,10), 5); ● lub wskazania innego konstruktora ● i jego parametrów Wydział Informatyki Politechniki Białostockiej, Cezary Bołdak Konstruktory jeszcze raz ● wskazanie właściwego konstruktora class Figura { Punkt srodek; public Figura(Punkt s) { srodek = new Punkt(s); } } class Kolo extends Figura { int r; public Kolo(Punkt s, int rr) { super(s); srodek=new Punkt(s); r=rr; } } ... Figura f=new Figura( new Punkt(0,0)); Kolo k = new Kolo ( new Punkt(10,10), 5); ● przy okazji inicjacja odziedziczonego pola ● który powinien być ... prywatny ? Wydział Informatyki Politechniki Białostockiej, Cezary Bołdak Hermetyzacja w dziedziczeniu ● czy prywatne pola/metody się dziedziczą ? class Figura { private Punkt srodek; public Figura(Punkt s) { srodek = new Punkt(s); } } class Kolo extends Figura { int r; public Kolo(Punkt s, int rr) { super(s); r=rr; } public void przesun(int dx, int dy) { srodek.przesun(dx, dy); BŁĄD HERMETYZACJI } ● dziedziczą się, ale są niedostępne !` ● metoda 'przesun' powinna być zdefiniowana w Figurze ● albo należy użyć nowego poziomu ochrony ... Wydział Informatyki Politechniki Białostockiej, Cezary Bołdak Poziom ochrony 'protected' ● pośredni poziom między 'public' a 'private' ● publiczny dla rodziny (w drzewie dziedziczenia) ● prywatny dla innych (obcych) klas class Figura { protected Punkt srodek; public Figura(Punkt s) { srodek = new Punkt(s); } } class Kolo extends Figura { int r; public Kolo(Punkt s, int rr) { super(s); r=rr; } public void przesun(int dx, int dy) { srodek.przesun(dx, dy); OK ` } ... Figura f=new Figura( new Punkt(0,0)); f.srodek=null; BŁĄD Kolo k = new Kolo ( new Punkt(10,10), 5); System.out.println(k.srodek); BŁĄD Wydział Informatyki Politechniki Białostockiej, Cezary Bołdak Dziedziczenie ● ● nie dziedziczy się: ● konstruktorów ● (destruktorów, operatora= w C++) dziedziczy się: ● pola ● metody ● typ Wydział Informatyki Politechniki Białostockiej, Cezary Bołdak ` Po co typ ? ● kompilator pilnuje zgodności typów: int i; float f; i = f; BŁĄD void f(int a) {...} f(”abc”); BŁĄD Samochód s = new Komputer(); ● BŁĄD ale czasami można go „oszukać” → rzutowanie (casting) ` i = (int)f; OK Samochód s = (Samochód)new Komputer(); Wydział Informatyki Politechniki Białostockiej, Cezary Bołdak BŁĄD Dziedziczenie typu ● ● klasa Kwadrat ma 4 typy (5): ● Figura ● Czworokat ● Prostokat ● Kwadrat Czworokąt więc kompilator dopuści podstawienie: Figura f = new Kwadrat(...); ● Figura rzutowanie w górę !!! (upcasting) Wydział Informatyki Politechniki Białostockiej, Cezary Bołdak ` Prostokąt Kwadrat Rzutowanie w górę Figura[] figury = new Figura[100]; figura[0] = new Kwadrat(...); figura[1] = new Prostokat(...); figura[2] = new Trojkat(...); ● rzutowanie to zachodzi tylko w dziedziczeniu ● zawsze jest możliwe i bezpieczne ` ● upraszcza przechowywanie obiektów z 1 rodziny Wydział Informatyki Politechniki Białostockiej, Cezary Bołdak Klasa Object ● ● dlaczego Kwadrat ma 5 typów ? każda klasa w Javie dziedziczy z klasy 'Object' (bezpośrednio lub pośrednio) class Figura {... } → class Figura extends Object {...} ● pojedyncze drzewo dziedziczenia ● składowe klasy Object: ● public String toString(); ● public boolean equals(Object o); ` ● ● metody związane z wątkami referencja typu 'Object' Object o = new ... Wydział Informatyki Politechniki Białostockiej, Cezary Bołdak Object Figura Czworokąt Prostokąt Kwadrat Utrata dostępu do informacji Figura[] figury; for (Figura f: figury) f.przesun(1, 0); ale Figura f = new Kolo(new Punkt(1,2), 10); System.out.println(f.getR()); ● skąd wiadomo że 'f' to Koło ? Figura f; if (random.nextInt(2)%2 == 0) f = new Kolo(new Punkt(1,2), 10); else f = new Prostokat(new Punkt(1,2), 4, 6); Wydział Informatyki Politechniki Białostockiej, Cezary Bołdak Utrata dostępu do informacji ● ● ● ● obiekt Koło cały czas jest w pamięci – nie zmienia typu !!! (obiekt raz stworzony jako Koło zawsze będzie już Kołem) rzutowanie w górę zmienia tylko sposób patrzenia na obiekt kompilator mając referencję 'Figura f' nie zna rzeczywistego typu obiektu na który ona wskazuje wie, że ten obiekt to jakaś Figura i ma metodę 'przesun(int,int)' Wydział Informatyki Politechniki Białostockiej, Cezary Bołdak f - srodek:Punkt +przesun(int,int) - r: int + getR(): int Rzutowanie w dół (downcasting) ● ● rzutowanie z typu bazowego na typ pochodny ale to zły przykład: Object Figura Kolo k = (Kolo)new Figura(...); ● dobry przykład: Figura f = new Kolo(...); Kolo k = (Kolo)f; ● poprawne rzutowanie w dół może nastąpić tylko po wcześniejszym rzutowaniu w górę ● odzyskanie utraconego dostępu do informacji ● rzutowanie w dół jest ryzykowne Wydział Informatyki Politechniki Białostockiej, Cezary Bołdak Czworokąt Prostokąt Kwadrat Mechanizm RTTI ● ● f identyfikacja typu w czasie wykonania programu (Run-Time Type Identification) umożliwia świadome i poprawne rzutowanie w dół if (f instanceof Kolo) { Kolo k = (Kolo)f; System.out.println(k.getR()); ● ● operator 'instanceof' bada, czy 'f ' ma typ 'Kolo' jego bezpośrednie używanie (nadużywanie) jest uważane za mało eleganckie Wydział Informatyki Politechniki Białostockiej, Cezary Bołdak ? Wykorzystanie 'instanceof' ● policzenie figur w tablicy Figura[] figury={ new Kwadrat(...), new Prostokat(...), new Czworokat(...)} int lczw, lprost, lkw=0; for (Figura f: figury) { if (f instanceof Kwadrat) ++ lkw; if (f instanceof Prostokat) ++ lprost; if (f instanceof Czworokat) ++ lczw; } System.out.println(”Kwadraty: ”+lkw); System.out.println(”Prostokąty: ”+lprost); System.out.println(”Czworokąty: ”+lczw); ● wynik: 1 2 3 !!! ● przecież każdy prostokąt jest też czworokątem !!! Wydział Informatyki Politechniki Białostockiej, Cezary Bołdak Wykorzystanie 'instanceof' ● alternatywne policzenie figur w tablicy ... if (f instanceof Kwadrat) ++ lkw; else if (f instanceof Prostokat) ++ lprost; else if (f instanceof Czworokat) ++ lczw; ... ● lub: if (f instanceof Kwadrat) ++ lkw; if ( f instanceof Prostokat && ! f instanceof Kwadrat) ++ lprost; if ( f instanceof Czworokat && ! f instanceof Prostokat) ++ lczw; Wydział Informatyki Politechniki Białostockiej, Cezary Bołdak Object Figura Czworokąt Prostokąt Kwadrat Nadpisywanie składowych ● w dziedziczeniu składowe dziedziczą się ● ale dopuszczalne jest ich nadpisywanie: class A { int x; } class B extends A { int x; } ● ● w takiej sytuacji obiekt B ma 2 zmienne 'x' !!! w przypadku pól to zwykle pomyłka Wydział Informatyki Politechniki Białostockiej, Cezary Bołdak B A x 3 x 5 Nadpisywanie pól class A { int x; } class B extends A { int x; } class C extends B { int x; public void showAll() { System.out.println("A.x="+A.x+",B.x="+super.x+",C.x="+x); } } C B A C c = new C(); c.x = 30; x 10 c.showAll(); ((B)c).x = 20; ((A)c).x = 10; x 20 c.ShowAll(); ● ważny jest typ referencji !!! Wydział Informatyki Politechniki Białostockiej, Cezary Bołdak x 30 Nadpisywanie metod ● ● ● ● ● w zasadzie nadpisywanie metod działa tak samo ale w Javie jest specjalny mechanizm, który komplikuje sprawę: POLIMORFIZM przy wywołaniu metod nie jest ważny typ referencji (jak w przypadku pól) ważny jest typ obiekty który się za nią ukrywa f ? metody wywoływane w ten sposób nazywane są metodami/funkcjami wirtualnymi Wydział Informatyki Politechniki Białostockiej, Cezary Bołdak Nadpisywanie a przeciążanie (overriding vs overloading) ● to nie jest nadpisywanie tylko przeciążanie: class A { String f() { return ”A.f”;} } class B extends A { String f(int a) { return ”B.f”;} } class C extends B { String f(char c) { return ”C.f”;} } C B A C c = new C(); System.out.println(c.f()); System.out.println(c.f(0)); System.out.println(c.f('a')); ● nadpisywanie: + A.f() + B.f(int) + C.f(char) C B A class A { String f() { return ”A.f”;} } class B extends A { String f() { return ”B.f”;} } class C extends B { String f() { return ”C.f”;} } System.out.println(c.f()); ??? + A.f() + B.f() + C.f() Wydział Informatyki Politechniki Białostockiej, Cezary Bołdak Wiązanie metod (binding) class A { public String } class B extends public String } class C extends public String } f() { return ”A.f”;} A { f() { return ”B.f”;} B { f() { return ”C.f”;} C B A + A.f(): String 0x02ca4 + B.f(): String + C.f(): String 0x02cc4 0x02ce4 A a = new C(); System.out.println(a.f()); ● obiekt klasy C ma 3 metody 'f()' ● każda ma inny adres w pamięci kodu ● kompilator musi wskazać, którą metodę wywołać ● skompilować znaczy przetłumaczyć na asembler: a.f(); → jump 0x02cc4 ? wiązanie metody Wydział Informatyki Politechniki Białostockiej, Cezary Bołdak Wiązanie wczesne i późne ● jeśli kompilator faktycznie wiąże metodę → wiązanie wczesne a.f(); → jump 0x02ca4 ● jaką metodę wybiera ? – ● z tej klasy, jakiego typu jest referencja 'a' (A) jeśli kompilator nie wiąże kodu → wiązanie późne a.f(); → jump _______ ● kiedy wykona się te wiązanie ? – ● w czasie wykonania programu (run-time) jaka metoda zostanie wybrana ? – z tej klasy, jakiego typu jest obiekt Wydział Informatyki Politechniki Białostockiej, Cezary Bołdak Funkcje/Metody wirtualne ● metody wiązane wcześnie – zwykłe metody ● metody wiązane późno – metody wirtualne ● mechanizm późnego wiązania metod – polimorfizm ● w Javie (prawie) wszystkie metody są wirtualne ● nie trzeba ich oznaczać (jak w C++ 'virtual') ● metody wirtualne mogą nieznacznie spowalniać wykonanie programu Wydział Informatyki Politechniki Białostockiej, Cezary Bołdak Figura Typowe użycie f. wirtualnych Trójkąt Czworokąt class Figura { public String opis(){return ”Jestem Figurą”;}} class Trojkat extends Figura { public String opis(){return ”Jestem Trójkątem”;}} class Czworokat extends Figura { public String opis(){return ”Jestem Czworokątem”;}} Prostokąt Kwadrat class Prostokat extends Czworokat { public String opis(){return ”Jestem Prostokątem”;}} class Kwadrat extends Prostokat { public String opis(){return ”Jestem Kwadratem”;}} ... Figura f=new ??? System.out.println(f.opis()); ... System.out.println(xxx.opis()); xxx Wydział Informatyki Politechniki Białostockiej, Cezary Bołdak ??? Metody niewirtualne ● w Javie wyjątkami (zwykłymi metodami) są metody: ● finalne ● statyczne ● prywatne class A { private String f() { return ”A.f”; } public String g() { return ”A.g;” } public String call() { return f()+” ”+g(); } } class B extends A { private String f() { return ”B.f”; } public String g() { return ”B.g;” } } ... A a = new B(); System.out.println(a.call()); Wydział Informatyki Politechniki Białostockiej, Cezary Bołdak Metody niewirtualne ● w Javie wyjątkami (zwykłymi metodami) są metody: ● finalne ● statyczne ● prywatne class A { wywołanie wirtualne private String f() { return ”A.f”; } public String g() { return ”A.g;” } public String call() { return f()+” ”+g(); } } wywołanie niewirtualne class B extends A { private String f() { return ”B.f”; } public String g() { return ”B.g;” } } ... A a = new B(); System.out.println(a.call()); → A.f B.g Wydział Informatyki Politechniki Białostockiej, Cezary Bołdak Jeszcze metody niewirtualne class A { private String f() { return ”A.f”; } public String g() { return ”A.g;” } } class B extends A { private String f() { return ”B.f”; } public String g() { return ”B.g;” } public String call() { return f()+” ”+g(); } } ... A a = new B(); System.out.println(a.call()); → ??? ● metoda 'call' w klasie B Wydział Informatyki Politechniki Białostockiej, Cezary Bołdak Jeszcze metody niewirtualne class A { private String f() { return ”A.f”; } public String g() { return ”A.g;” } } class B extends A { wywołanie wirtualne private String f() { return ”B.f”; } public String g() { return ”B.g;” } public String call() { return this.f()+” ”+this.g(); } } ... wywołanie niewirtualne A a = new B(); System.out.println(a.call()); → B.f B.g ● 'this' w kodzie klasy A jest typu A ● 'this' w kodzie klasy B jest typu B Wydział Informatyki Politechniki Białostockiej, Cezary Bołdak Poziom kodu class A { protected int x = 10; public int getAx() { return x; } } class B extends A { x 10 protected int x = 20; public int getBx() { return x; } } x 20 class C extends B { protected int x = 30; public int getCx() { return x; } x 30 } ... C c= new C(); System.out.println(””+c.getAx()+c.getBx()+c.getCx()); Wydział Informatyki Politechniki Białostockiej, Cezary Bołdak C B A → Poziom kodu A class A { typu A #x protected int x = 10; +getAx() public int getAx() { return this.x; } } class B extends A { typu B B protected int x = 20; #x public int getBx() { return this.x; } +getBx() } class C extends B { typu C protected int x = 30; C public int getCx() { return this.x; } #x } +getCx() ... C c= new C(); System.out.println(””+c.getAx()+c.getBx()+c.getCx()); → 10 20 30 ● kod sięga do pola ze swojemu poziomu ● potem do pól odziedziczonych (do góry) ● nigdy nie sięga w dół (do klas dziedziczących) Wydział Informatyki Politechniki Białostockiej, Cezary Bołdak Klasy i metody finalne ● z klasy finalnej nie można dziedziczyć finall class Ostateczna { ... } class Nowa extends Ostateczna { ... } ● ● metody finalnej nie można nadpisać jest też metodą niewirtualną (m.in. szybciej się wykonuje – wiązanie wczesne) class Prostokat { protected double a, b; public final double pole() { return a*b; } public Prostokat(double aa, double bb) { a=aa; b=bb; } } class Kwadrat extends Prostokat { public Kwadrat(int a) { super(a, a); } public double pole() { return ... } } Wydział Informatyki Politechniki Białostockiej, Cezary Bołdak Klasy abstrakcyjne ● z klasy abstrakcyjne nie można utworzyć obiektu abstract class Figura { ... } new Figura(...); ● klasa abstrakcyjna służy tylko do dziedziczenia class Kolo extends Figura { ... } class Trojkat extends Figura { ... } ● ale można tworzyć referencje typu abstrakcyjnego Figura f = null; Figura[] figury = new Figura[100]; f=new Kolo(...); figury[0] = new Trojkat(...); ● ● klasa abstrakcyjna to zwykła klasa: może mieć pola, metody, konstruktory klasa abstrakcyjna ma 1 przywilej: tylko ona może posiadać metody abstrakcyjne → ... Wydział Informatyki Politechniki Białostockiej, Cezary Bołdak Metody abstrakcyjne ● metoda abstrakcyjna to metoda niedokończona, zawiera tylko nagłówek abstract class Figura { protected Punkt srodek; public abstract double pole(); } ● ● używana kiedy jej definicja nie miałaby sensu (jak policzyć pole jakiejś nieokreślonej figury ?) klasa która dziedziczy z klasy abstrakcyjnej metodę abstrakcyjną musi ją zdefiniować do końca class Kolo extends Figura { protected double r; public double pole() { return Math.PI*r*r; } } ● metoda abstrakcyjna nigdy nie będzie wywoływana: jest wirtualna a nigdy nie stworzy się obiekt klasy Figura Wydział Informatyki Politechniki Białostockiej, Cezary Bołdak Po co metody abstrakcyjne ? ● kompilator ma wywołanie: Figura f = ... f.pole(); ● ● musi sprawdzić, czy w klasie Figura metoda 'pole()' nie jest np.: statyczna, finalna, prywatna → niewirtualna metoda abstrakcyjna narzuca na klasy dziedziczące obowiązek posiadania kompletnej metody 'pole()' (jeśli same nie chcą być abstrakcyjne): abstract class Figura2D extends Figura { } ● metoda abstrakcyjna definiuje interfejs hierarchii klas Wydział Informatyki Politechniki Białostockiej, Cezary Bołdak Wywoływanie metod zastępowanych ● konstruktor klasy dziedziczącej woła konstruktor klasy bazowej ● nowa metoda wirtualna całkowicie zastępuje przesłanianą metodę ● metoda zastępujące może po raz ostatni wywołać metodę zastępowaną class Figura { public String opis() {return ” Jestem Figurą”;} } class Kolo extends Figura { public String opis(){return super.opis()+” i Kołem”;} } ... Figura f=new Koło(); System.out.println(f.opis()); → Jestem Figurą i Kołem Wydział Informatyki Politechniki Białostockiej, Cezary Bołdak Metoda 'public String toString()' ● metoda zdefiniowana w klasie 'Object' ● oryginalna definicja wypisuje rzeczywistą nazwę klasy i nr referencji ● zwraca tekstową reprezentację obiektu ● często wykorzystywana class Punkt extends Object { private x, y; } ... Punkt p=new Punkt (1,2); System.out.print(p);<==>System.out.print(p.toString()); → Punkt@0x23cd ● można ją nadpisać i w nowej metodzie wywołać oryginalną definicję: class Punkt extends Object { private x, y; public String toString() { return super.toString+”(x:”+x+”,y:”+y+”)”; } } System.out.print(p);→ Punkt@0x23cd(x:1,y:2) Wydział Informatyki Politechniki Białostockiej, Cezary Bołdak Hermetyzacja a funkcje wirtualne ● przy nadpisywaniu metod wirtualnych nie można zmniejszyć poziomu dostępności (zwiększyć poziomu ochrony): class Figura { public String opis(){return ”Jestem Figurą”;} } class Kolo extends Figura { protected String opis(){return ”Jestem Kołem”;} } ● // BŁĄD kompilacji poziom ochrony (dostępności) należy co najmniej zachować class Kolo extends Figura { public String opis(){return ”Jestem Kołem”;} } Wydział Informatyki Politechniki Białostockiej, Cezary Bołdak // BŁĄD kompilacji