Informatika4-2018/Gyakorlat6
a (→Nem statikus) |
a (→Interface) |
||
(egy szerkesztő 7 közbeeső változata nincs mutatva) | |||
1. sor: | 1. sor: | ||
+ | [[Informatika4-2018/Gyakorlat5|Előző]] - [[Informatika4-2018#Gyakorlat|Fel]] - [[Informatika4-2018/Gyakorlat7|Következő]] | ||
+ | |||
== Overriding == | == Overriding == | ||
=== Nem statikus === | === Nem statikus === | ||
67. sor: | 69. sor: | ||
Ekkor hiába '''Child''' konstruktorral jött létre egy '''Parent''' típusú változó, akkor is a '''Parent.f''' hívódik meg. | Ekkor hiába '''Child''' konstruktorral jött létre egy '''Parent''' típusú változó, akkor is a '''Parent.f''' hívódik meg. | ||
<java> | <java> | ||
− | Parent objects = new Parent[2]; | + | Parent[] objects = new Parent[2]; |
objects[0] = new Parent(); | objects[0] = new Parent(); | ||
objects[1] = new Child(); // leszármazott osztály az ősosztállyá konvertálódik | objects[1] = new Child(); // leszármazott osztály az ősosztállyá konvertálódik | ||
77. sor: | 79. sor: | ||
Vagyis a statikus felüldefiniálás elvész, az számít, hogy mely típus statikus metódusát hívjuk, nem pedig az hogy a konkrét példány hogyan jött létre. | Vagyis a statikus felüldefiniálás elvész, az számít, hogy mely típus statikus metódusát hívjuk, nem pedig az hogy a konkrét példány hogyan jött létre. | ||
+ | |||
+ | === Tagváltozó === | ||
+ | Az ugyanolyan nevű (nem statikus) tagváltozók a leszármazott osztályban '''elfedik''' az öröklött tagváltozót. | ||
+ | <java> | ||
+ | public class A | ||
+ | { | ||
+ | protected int a_; | ||
+ | } | ||
+ | |||
+ | public class B extends A | ||
+ | { | ||
+ | protected int a_; // ekkor van A.a_ és B.a_ is | ||
+ | public void f() | ||
+ | { | ||
+ | a_ // ez B.a_ | ||
+ | this.a_ // ez is B.a_ | ||
+ | super.a_ // ez A.a_ | ||
+ | } | ||
+ | } | ||
+ | </java> | ||
+ | Ezt könnyű elvéteni és zavaró is, ezért ''ellenjavallott''! | ||
+ | |||
+ | Még könnyebb elrontani, ha van egy ''ugyanolyan nevű lokális változó''nk is. Szintén ellenjavallott. | ||
+ | <java> | ||
+ | public class B extends A | ||
+ | { | ||
+ | protected int a_; | ||
+ | public void f(float a_) | ||
+ | { | ||
+ | a_ // ez a függvény argumentuma | ||
+ | this.a_ // ez B.a_ | ||
+ | super.a_ // ez A.a_ | ||
+ | } | ||
+ | } | ||
+ | </java> | ||
== Interface == | == Interface == | ||
+ | |||
+ | Definiáljuk az alábbi '''interface'''-t | ||
+ | <java> | ||
+ | public interface Polygon | ||
+ | { | ||
+ | public float area(); | ||
+ | public void translate(float x, float y); | ||
+ | } | ||
+ | </java> | ||
+ | Két osztály '''implementál'''ja ezt: '''Square''' és '''LineSegment'''. Valahogy így: | ||
+ | <java> | ||
+ | public class Square implements Polygon | ||
+ | { | ||
+ | private float x_, y_, a_; | ||
+ | public Square() | ||
+ | { | ||
+ | ... | ||
+ | } | ||
+ | . | ||
+ | . | ||
+ | . | ||
+ | public float area() | ||
+ | { | ||
+ | ... | ||
+ | } | ||
+ | public void translate(float x, float y) | ||
+ | { | ||
+ | ... | ||
+ | } | ||
+ | } | ||
+ | </java> | ||
+ | Hasonlóan a '''LineSegment'''-re (annak mindig 0 a területe). | ||
+ | |||
+ | Ekkor lehetőségünk van ezeket a síkbeli objektumokat egységesen kezelni. | ||
+ | <java> | ||
+ | Polygon[] polygons = new Polygon[2]; | ||
+ | polygons[0] = new Square(0,0,1); | ||
+ | polygons[1] = new LineSegment(); | ||
+ | |||
+ | polygons[0].translate(-1,-1); | ||
+ | polygons[1].translate(1,1); | ||
+ | </java> | ||
+ | Figyeljük meg, hogy ekkor nem tudok egy '''Polygon''' példányt látrehozni, annélkül hogy Square vagy LineSegment ne lenne. | ||
+ | <java> | ||
+ | Polygon p = new Polygon(); // <- hiba! | ||
+ | </java> | ||
+ | Ez azért van, mert az interface-ek ú.n. ''absztrakt osztály''ok, a metódusai (konstruktora is) csak ígéretek arra, hogy van olyan metódusa, de implemntálva nincsen. | ||
+ | Vagyis a metódusai a leszármazott osztályokban vannak implementálva. | ||
+ | |||
+ | == Feladat == | ||
+ | Öröklődéssel (és esetleg interface-el) érjük el, hogy legyen immutable és nem immutable osztályunk is a négyzetre és a szakaszra! | ||
+ | |||
+ | [[Informatika4-2018/Gyakorlat5|Előző]] - [[Informatika4-2018#Gyakorlat|Fel]] - [[Informatika4-2018/Gyakorlat7|Következő]] |
A lap jelenlegi, 2019. november 4., 16:07-kori változata
Tartalomjegyzék |
Overriding
Nem statikus
Ha egy osztályban van egy ugyanolyan szignatúrájú metódus, mint az ősében, akkor beszélünk
- felülírás, felüldefiniálás
- override
- túlterhelés
-ről
public class Parent { float f(float x) { ... } } public class Child extends Parent { float f(float x) { ... } }
Ekkor az alábbi f hívások mást adnak vissza, attól függően, hogy hogyan írtuk felül.
Parent parent = new Parent(); Child child = new Child(); parent.f(3.14f); // <- szülőben lévő f child.f(3.14f); // <- gyerekben lévő f
Viszont ez akkor is így történik, ha minkettőt az ősosztlyként tárolom.
Parent objects = new Parent[2]; objects[0] = new Parent(); objects[1] = new Child(); // leszármazott osztály az ősosztállyá konvertálódik objects[0].f(3.14f); // <- szülőben lévő f objects[1].f(3.14f); // <- gyerekben lévő f
Vagyis az íly módon meghívott f őrzi azt, hogy hogyan hoztuk létre az aktuális példányt.
Ez a dinamikus polimorfizmus és a hatását egy felüldefiniált metóduson keresztül láthattuk.
Statikus
Nem ez a helyzet statikus metódusnál.
public class Parent { static float f(float x) { ... } } public class Child extends Parent { static float f(float x) { ... } }
Ekkor hiába Child konstruktorral jött létre egy Parent típusú változó, akkor is a Parent.f hívódik meg.
Parent[] objects = new Parent[2]; objects[0] = new Parent(); objects[1] = new Child(); // leszármazott osztály az ősosztállyá konvertálódik objects[0].f(3.14f); // <- Parent.f objects[1].f(3.14f); // <- Parent.f
Figyeljük meg, hogy statikus metódust osztályra szoktunk hívni (Parent.f vagy Child.f), ezért warning-ot kapunk, de akkor is ugyanaz történik.
Vagyis a statikus felüldefiniálás elvész, az számít, hogy mely típus statikus metódusát hívjuk, nem pedig az hogy a konkrét példány hogyan jött létre.
Tagváltozó
Az ugyanolyan nevű (nem statikus) tagváltozók a leszármazott osztályban elfedik az öröklött tagváltozót.
public class A { protected int a_; } public class B extends A { protected int a_; // ekkor van A.a_ és B.a_ is public void f() { a_ // ez B.a_ this.a_ // ez is B.a_ super.a_ // ez A.a_ } }
Ezt könnyű elvéteni és zavaró is, ezért ellenjavallott!
Még könnyebb elrontani, ha van egy ugyanolyan nevű lokális változónk is. Szintén ellenjavallott.
public class B extends A { protected int a_; public void f(float a_) { a_ // ez a függvény argumentuma this.a_ // ez B.a_ super.a_ // ez A.a_ } }
Interface
Definiáljuk az alábbi interface-t
public interface Polygon { public float area(); public void translate(float x, float y); }
Két osztály implementálja ezt: Square és LineSegment. Valahogy így:
public class Square implements Polygon { private float x_, y_, a_; public Square() { ... } . . . public float area() { ... } public void translate(float x, float y) { ... } }
Hasonlóan a LineSegment-re (annak mindig 0 a területe).
Ekkor lehetőségünk van ezeket a síkbeli objektumokat egységesen kezelni.
Polygon[] polygons = new Polygon[2]; polygons[0] = new Square(0,0,1); polygons[1] = new LineSegment(); polygons[0].translate(-1,-1); polygons[1].translate(1,1);
Figyeljük meg, hogy ekkor nem tudok egy Polygon példányt látrehozni, annélkül hogy Square vagy LineSegment ne lenne.
Polygon p = new Polygon(); // <- hiba!
Ez azért van, mert az interface-ek ú.n. absztrakt osztályok, a metódusai (konstruktora is) csak ígéretek arra, hogy van olyan metódusa, de implemntálva nincsen. Vagyis a metódusai a leszármazott osztályokban vannak implementálva.
Feladat
Öröklődéssel (és esetleg interface-el) érjük el, hogy legyen immutable és nem immutable osztályunk is a négyzetre és a szakaszra!