Objektumorientáltság a Javában

Kategória: Standard Java.

Arról már volt szó, hogy a Java-ban mindent osztályba kell tenni, nincsenek globális függvények, változók. A programozás oldalon, az objektumorientált programozás alfejezetben leírtak itt is érvényesek; az alapelvek Java-specifikus pontosításai találhatóak itt. A Java-ban igen sok finomhangolásra van lehetőség.

Osztály (class)

Egy Java forrásfájl egy publikus osztályt tartalmaz, aminek a neve meg kell, hogy egyezzen a fájl nevével. Az osztály deklarációja a következő: public class [Osztálynév], majd kapcsos zárójelben jönnek az attribútumok (azaz osztály szintű változók) és metódusok (osztály szintű függvények). Ahhoz, hogy egy osztályt használni tudjunk, példányosítanunk kell a new kulcsszóval. Az osztály egy példányát objektumnak hívjuk. Az osztály egy elemére ponttal . hivatkozunk.

A következő példához hozzuk létre a MyClass.java és a Main.java forrásokat az alábbi tartalommal:

// MyClass.java
public class MyClass {
    private int param1;
    private int param2;
 
    public MyClass() {
        param1 = 0;
        param2 = 0;
    }
 
    public MyClass(int param1, int param2) {
        this.param1 = param1;
        this.param2 = param2;
    }
 
    public int getSum() {
        return param1 + param2;
    }
 
    public int getProduct() {
        return param1 * param2;
    }
}
 
// Main.java
public class Main {
    public static void main(String[] args) {
        MyClass myClass = new MyClass(2, 3);
        System.out.println(myClass.getSum());
        System.out.println(myClass.getProduct());
    }
}

A visszatérési érték nélküli eljárás, aminek a neve megegyezik az osztály nevével, a konstruktor, melyről lesz még szó később. A this kulcsszó az adott példányra hivatkozik (a konstruktorban ugyanis már meg van a példány).

Tetszőleges osztály tetszőleges példánya felveheti a null értéket, ami az adat hiányára utal.

Öröklődés (inheritence)

Az öröklődést Java-ban az extends kulcsszóval tudjuk megvalósítani. Tekintsük az aábbi példát!

// BaseClass.java
public class BaseClass {
    int baseAttribute;
    void baseFunction() {}
}
 
// SubClass.java
public class SubClass extends BaseClass {
    int subAttribute;
    void subFunction() {}
}

A SubClass nevű osztály a BaseClass leszármazottja, így örökli azok attribútumait és metódusait. A SubClass tehát valójában 2 attribútumot és 2 metódust tartalmaz: egyrészt megörökli a BaseClass-ból a baseAttribute nevű attribútumot és baseFunction nevű metódust, másrészt definiál egy saját attribútumot (subAttribute) és saját metódust (subFunction). Így aki a SubClass-t példányosítja, az mind a 4 elemet eléri. (Ill. hogy pontosan mit ér el és mit nem, azt lejjebb találjuk, a láthatóságról szóló szakaszban.)

A Java-ban öröklődés mindig van. Ha nem írjuk ki, akkor az az osztály a java.lang.Object osztályból öröklődik.

A Java-ban nem lehetséges a többszörös öröklődés. Azokban az esetekben, amelyekben erre lenne szükség, az alábbi lehetőségek közül választhatunk:

  • Leszármazás helyett hivatkozzunk azokra az osztályokra, amelyeket használni szeretnénk. Ebben az esetben lehetséges, hogy a hivatkozott osztályt is módosítanunk kell, pl. ha egy védett (protected) eljárást szeretnénk meghívni.
  • Ha mindenképpen örökölni érdemes akkor kihasználhatjuk azt, hogy egy osztály akármennyi interfészt megvalósíthat (az interfészeket ld. lejjebb). Ehhez elképzelhető, hogy át kell szervezni az osztályhierarchiát.

Felüldefiniálás (override)

A Java programozási nyelvben alapból mindegyik metódus felüldefiniálható. Lássuk az alábbi példát!

// BaseClass.java
public class BaseClass {
    int myFunction(int a, int b) {
        return a + b;
    }
}
 
// SubClass.java
public class SubClass extends BaseClass {
    int myFunction(int a, int b) {
        return a * b;
    }
}
 
// Main.java
public class Main {
    public static void main(String[] args) {
        BaseClass baseClass = new BaseClass();
        SubClass subClass = new SubClass();
        BaseClass baseSubClass = new SubClass();
 
        System.out.println(baseClass.myFunction(2, 3));
        System.out.println(subClass.myFunction(2, 3));
        System.out.println(baseSubClass.myFunction(2, 3));
    }
}

Az alaposztályban definiálunk egy két paraméterű függvényt, ami a paraméterek összegével tér vissza. A leszármazott ezt felüldefiniálja (figyeljük meg, hogy a függvény szignatúrája ugyanaz), és a paraméterek szorzatával tér vissza. A főprogramban háromféleképpen használjuk a fenti függvényt:

  • Az őszosztályt példányosítjuk: ez esetben az ősosztály függvénye fut le, és az összeg lesz az eredmény.
  • A leszármazott osztályt példányosítjuk: ez esetben a leszármazott osztály függvénye fut le, és a szorzat lesz az eredmény.
  • A leszármazott osztályt példányosítjuk, de egy ősosztály típusú változónak adjuk át az értéket. (Ez megengedett az objektumorientált programozásban, sőt, ez adja az objektumorientált programozás egyik lényegét.) Az ősosztályon hívjuk meg a függvényt, az eredmény mégis a szorzat lesz, mert a példányosított objektum tényleges (dinamikus) típusa a leszármazott, és a felüldefiniált metódus fut le.

Megjegyzés: egyes "szigorú" Java fordítók figyelmeztetést adnak akkor, ha felüldefiniálunk egy metódust, mert nem biztos abban, hogy véletlenül tettük-e vagy szándékosan. (Gondoljunk egy összetett osztályhierarchiára, melynek ősosztályaiban rengeteg függvény van; az egyik leszármazottban véletlenül is választhatunk pont ugyanolyan szignatúrát). Az annotációkról még lesz szó részletesebben az Enterprise Java részben; ezek @ karakterrel kezdődő jelölők, ami többnyire a futtató rendszer számára hordoz információt. Annak jelölésére, hogy mi valóban felül szerettük volna definiálni a metódust, a @Override annotációval tudjuk jelezni, a következőképpen:

public class SubClass extends BaseClass {
    @Override
    int myFunction(int a, int b) {
        return a * b;
    }
}

Túlterhelés (overload)

A felüldefiniálást gyakran összetévesztik a túlterheléssel, melyen az angol nevük hasonlósága (override - overload) sem segít. Ez utóbbi azt jelenti, hogy egy adott osztályon belül van kettő vagy több olyan metódus, melynek ugyanaz a neve, de eltérő a paraméterlistája. Például:

public class OverloadExample {
    public int increment(int a, int b) {
        return a + b;
    }
 
    public int increment(int a) {
        return a + 1;
    }
 
    public static void main(String[] args) {
        OverloadExample oe = new OverloadExample();
        System.out.println(oe.increment(2, 3));
        System.out.println(oe.increment(2));
    }
}

Konstruktor

Java-ban a konstruktor neve megegyezik az osztály nevével, visszatérési értéke nincs. Paramétereket kaphat, és egy osztálynak akárhány konstruktora lehet. Ha egy osztálynak nincs konstruktora, akkor a fordító generál egyet, ami publikus és paraméter nélküli; ezt alapértelmezett konstruktornak (default constructor) nevezzük. Egy konstruktor első utasítása mindig a this(…) vagy a super(…); az előbbi az adott osztály egy másik konstruktorát hívja, az utóbbi pedig az ősosztály konstruktorát. Ezek csak első utasításként szerepelhetnek, egyszerre pontosan egy. Ha nem írunk oda semmit, akkor automatikusan generálódik egy super() (paraméter nélkül). Ez utóbbi egyúttal azt is jelenti, hogy ha nincs az ősosztálynak egy paraméter nélküli konstruktora, akkor nem tudunk konstruktor nélküli leszármazottat létrehozni.

Az alábbi példa két konstruktort tartalmaz: egy alapértelmezettet, ami meghívja a két paraméterrel rendelkezőt:

public class ConstructorExample {
    private int a;
    private int b;
 
    public ConstructorExample(int a, int b) {
        this.a = a;
        this.b = b;
    }
 
    public ConstructorExample() {
        this(0, 0);
    }
}

Igen gyakori egyébként, hogy a paramétereket tartalmazó konstruktor az osztály attribútumainak ad értéket.

A Java-ban hagyományos értelembe vett destruktor nincs. Van egy void finalize() szignatúrájú függvény a java.lang.Object osztályban, ami így mindegyik osztályban szerepel, ezt felül tudjuk definiálni, és a Java szemétgyűjtő (garbage collector) meghívja ezt a függvényt, mielőtt megszüntetné az osztályt, ezt viszont ritkán használjuk.

Interfészek

Interfészt a Java-ban az interface kulcsszóval hozhatunk létre, az osztályhoz hasonlóan. A fájl nevének meg kell egyeznie az interfész nevével. Interfészen belül lehetnek függvény fejlécek (ami lehet statikus vagy nem statikus), valamint konstansok, azaz static és final kulcsszóval ellátott változók, melynek azonnal értéket kell adni. A metódusoknak nincs megvalósítása, azokat pontosvesszővel le kell zárni. Mindegyik elem automatikusan publikus, így a public kulcsszót nem kell kiírni (bár ki szabad), ill. a változó automatikusan public static final lesz. Egy interfész akármennyi más interfészt kiterjeszthet az extends kulcsszóval; a kiterjesztendő interfészek listáját vesszővel kell elválasztani. Az interfészeket nem lehet példányosítani.

Egy osztály akárhány interfészt megvalósíthat, amit az implements kulcsszóval adhatunk meg az osztály deklarációjában, az esetleges osztály kiterjesztést (extends) követően. Lássunk egy példát!

// InterfaceExample.java
public interface InterfaceExample {
    int add(int a, int b);
    int multiply(int a, int b);
}
 
// ImplementationExample.java
public class ImplementationExample implements InterfaceExample {
    public int add(int a, int b) {
        return a + b;
    }
 
    public int multiply(int a, int b) {
        return a * b;
    }
}
 
// Main.java
public class Main {
    public static void main(String[] args) {
        InterfaceExample interfaceExample = new ImplementationExample();
        System.out.println(interfaceExample.add(2, 3));
        System.out.println(interfaceExample.multiply(2, 3));
    }
}

A példában a változó statikus típusa az interfész, és a megvalósítást példányosítottuk. Igen gyakori, hogy pl. egy függvény paramétere interfész típusú, és megvalósítást adunk át neki.

Felsorolás

A Java-ban létezik felsorolás típus, amit az enum kulcsszóval lehet létrehozni. Hasonlóan működik, min a class vagy az interfész: a forrásfájl nevének meg kell egyeznie az enum nevével, a kiterjesztés itt is .java, de máshol, pl. osztályon belül is létre tudjuk hozni. Az enum konstansok vesszővel elválasztott felsorolása, amelyek implicit módon statikusak, publikusak és programból megváltoztathatatlanok. Szokás szerint a lista elemeit csupa nagybetűvel írjuk. A felsorolás egy konkrét elemére ponttal (.) hivatkozunk. Gyakran a már bemutatott switch utasítással együtt szoktuk használni.

Az alábbi példa osztályon belül hoz léte egy felsorolást, ami a hét napjait tartalmazza, majd egy függvény a paraméterül kapott nap függvényében kiír valamit. A főprogram egy konkrét nappal meghívja.

public class Main {
    public enum Day {
        MONDAY,
        TUESDAY,
        WEDNESDAY,
        THURSDAY,
        FRIDAY,
        SATURDAY,
        SUNDAY
    }
 
    static void printDay(Day day) {
        System.out.println(day);
        switch (day) {
        case MONDAY:
            System.out.println("The week has just started. Bad luck...");
            break;
        case TUESDAY:
            System.out.println("Onde day already passed this week!");
            break;
        case WEDNESDAY:
            System.out.println("Middle of the working week.");
            break;
        case THURSDAY:
            System.out.println("The weekend is slowly coming...");
            break;
        case FRIDAY:
            System.out.println("This is the last working day this week!");
            break;
        case SATURDAY:
            System.out.println("The weekend has started! Enjoy!");
            break;
        case SUNDAY:
            System.out.println("Enjoy the weekend, but prepare for the next week.");
            break;
        default:
            System.out.println("Definitely there must be an error!");
        }
    }
 
    public static void main(String[] args) {
        printDay(Day.FRIDAY);
    }
}

A fenti példa a switch … case … default struktúrát is jól illusztrálja: mindegyik case ágat egy break utasítás zár le, és van default is.

Csomagok

A Java az osztályok nagyobb logikai egységbe szervezését a csomagok (package) létrehozásával oldotta meg. A csomagokat fastruktúrába szervezhetjük, pontosan úgy, ahogy a fájlok szerepelnek a fájlrendszerben. Az egyes elemeket ponttal (.) választjuk el. A Java szigorú rendet vár el: a csomagstruktúrának pontosan meg kell felelnie a forrásoknak a fájlrendszerben elfoglalt helyével. Tehát pl. a package1.package2.package3.MyClass osztályt megvalósító forrásfájl (melynek neve MyClass.java) a package3 nevű könyvtárban kell, hogy legyen, ami a package2 könyvtárban ami végül a package1 könyvtárban.

A forrásfájl első kötelező utasítása a package, amely megmondja, hogy az adott osztály mely csomagban van. (Idáig amiatt nem szerepelt, mert ha a gyökérbe tesszük a forrást, akkor nem kell megadni - ez az egyetlen kivétel. Az egy-két osztályból álló példaprogramokat lehet "ömleszteni", de nagy általánosságban elmondható, hogy célszerű létrehozni csomaghierarchiát.)

Csomagon belül minden nehézség nélkül tudunk más elemre (pl. osztályra, interfészre) hivatkozni. Csomagon kívül viszont meg kell adnunk, hogy hol található az adott elem definíciója. Ezt az import kulcsszóval tudjuk megadni, amely tipikusan a package után következik, és a gyakorlatban gyakran több tucat elemet tartalmaz (mindegyik import új sorba kerül.) Erre amiatt van szükség, mert ugyanolyan nevű osztályok lehetnek az elérhető osztályok között (az ún. classpath-on; pl. csak abból, hogy Process, van nálam 3), és meg kell tudnunk mondani, hogy melyiket szeretnénk használni. Elvben az is előfordulhat, hogy a különböző helyen levű, ugyanolyan nevű osztályokat szeretnénk használni; ez esetben egyiket sem importjuk, hanem a használat helyén megadjuk a teljes elérési útvonalat.

Általában egyesével adjuk meg az osztályok importját, de a nyelv megengedi azt is, hogy adott csomagon belül az összes osztályt importáljuk; ez esetben csillagot (*) kell tenni az import utasítás végére. A Java nyelv alaposztályai a java.lang csomagon belül vannak, azokat nem kell importálni; ezt úgy képzeljük el, mintha mindegyik forrásban lenne egy implicit import java.lang.*; utasítás. Ilyen osztály pl. az Object, a String és az Exception.

Az alábbi példa a csomagok használatát illusztrálja:

// com/mycompany/mypackage/MyClassOne.java
package com.mycompany.mypackage;
 
import com.mycompany.myanotherpackage.MyClassTwo;
 
public class MyClassOne {
    MyClassTwo myclassTwo;
}
 
// com/mycompany/myanotherpackage/MyClassTwo.java
package com.mycompany.myanotherpackage;
 
public class MyClassTwo {}

Az import azt sugallja, mintha betöltene valamit; valójában azon kívül nem csinál mást, mint jelzi a fordítónak, hogy az adott osztály hol található. Tehát nem ekvivalens a C+ #include utasításával, ami valóban betölti a fejlécet és a függőségeit, kifejti a makrókat stb. A puszta jelölésnél annyival több haszna van ennek, hogy már a fejlécben kiderül az esetleges fordítási hiba, ha nem elérhető a betöltendő osztály.

A példában látható még az a gyakorlat, hogy milyen konvenciót érdemes használni a csomag elnevezése során: az első 2 szint a szoftverfejlesztő cégre vonatkozik (a honlapja URL-ének a fordítottja), utána általában a fő projekt neve következik, és csak ezt követően jönnek az adott feladatot megvalósító komponensekhez tartozó csomagok.

Ezzel máris elérkeztünk a csomagokhoz képest is nagyobb logikai egységekhez: a könyvtárakhoz, melyek kiterjesztése .jar, és a megfelelő csomagnév adással azok is hierarchiába szerveződnek.

Láthatóság

A fenti példákban többször találkoztunk (idáig magyarázat nélkül) a public és private kulcsszót. Egy osztályon belül attribútumok, metódusok és egyéb egyéb elemek esetén az alábbiakat használhatjuk:

Mindegyik attribútumnak és metódusnak van láthatósága, ami Java-ban az alábbi 4 lehetőség egyike lehet:

  • public: publikus, bármely másik osztály elérheti.
  • protected: a leszármazott osztályok, valamint az adott csomagban szereplő többi osztály elérheti, a többi nem.
  • alapértelmezett: csak az adott csomagban szereplő osztályok érhetik el.
  • private: csak az adott osztály érheti el.
public class AccessModifiers {
    private int myPrivateAttribute;
    int myPackagePrivateAttribute;
    protected void myProtectedFunction() {}
    public void myPublicMethod() {}
}

Több osztályt tartalmazó forrásfájlok

Egy forrásfájl több osztályt is tartalmazhat, de ezek közül csak egy lehet publikus: melynek neve megegyezik a fájl nevével.

public class PublicClass {}
class PrivateClass1 {}
class PrivateClass2 {}

Noha a nyelv megengedi ezt célszerű elkerülni, és ragaszkodni az egy forrás - egy osztály felálláshoz.

Belső osztályok

A Java-ban osztályon belül is deklarálhatunk osztályt, ezt belső osztálynak (angolul inner class) nevezzük. Ez nem magához az osztályhoz, hanem annak egy példányához tartozik, így a belső osztály példányosításához először a befoglaló osztályt kell példányosítani. A belső osztály eléri a külső osztály attribútumait és metódusait.

// MyClass.java
public class MyClass {
    class MyInnerClass {
        ...
    }
    ...
}
 
// Main.java
public class Main {
    public static void main(String[] args) {
        MyClass myClass = new MyClass();
        MyClass.MyInnerClass myInnerClass = myClass.new MyInnerClass();
        ...
    }
}

Az osztályon belüli osztály lehet statikus (static class MyInnerClass); ez esetben a példányosításhoz nem kell osztálypéldány, hanem csak maga az osztály (MyClass.MyInnerClass myInnerClass = new MyClass.MyInnerClass();).

Absztrakt osztályok és függvények

Az eddigi osztályok mind példányosíthatóak voltak. Előfordulhat azonban az, hogy a fejlesztő nem szeretné, ha az osztályát bárki is közvetlenül példányosítaná, leginkább amiatt, mert egy-egy függvény nincs megvalósítva, anélkül pedig hibás működést eredményezne. Ez esetben kötelező leszármaztatni az adott osztályból, megvalósítani a hiányzó metódusokat, és a leszármazottakat tudjuk példányosítani. Hivatkozni persze továbbra is tudunk az absztrakt ősosztályra. Egy absztrakt osztály leszármazottja is maradhat absztrakt; ez esetben abból kell leszármaztatni.

Az abstract kulcs szóval tudjuk jelezni azt, hogy az osztály absztrakt. Az absztrakt osztályokban szereplő absztrakt függvényeket szintén az abstract kulcsszóval jelezzük; ennek nincs megvalósítása (még üres sem), azt pontosvesszővel (;) kell lezárni.

Az alábbi példa az absztrakt osztály mechanizmust illusztrálja.

// AbstractClass.java
public abstract class AbstractClass {
    public int add(int a, int b) {
        return a + b;
    }
 
    public abstract int multiply(int a, int b);
}
 
// ConcreteClass.java
    public class ConcreteClass extends AbstractClass {
    @Override
    public int multiply(int a, int b) {
        return a * b;
    }
}
 
// Main.java
public class Main {
    public static void main(String[] args) {
        AbstractClass abstractExample = new ConcreteClass();
        System.out.println(abstractExample.multiply(2, 3));
    }
}

Lényeges különbség az absztrakt osztályok és az interfészek között, hogy az absztrakt osztályokban van üzleti logika, azok "majdnem" példányosíthatóak, általában csak egy-két függvényt kell megvalósítani, míg az interfészek esetén tényleg csak interfészről beszélünk, és mindent meg kell valósítani.

Név nélküli osztályok

Az osztályok létrehozása Java-ban nehézkes: létre kell hozni külön forrásfájlt, gyakran az se nyilvánvaló, hogy melyik csomagba kerüljön (esetleg csomagot és létre kell hozni számára), és előfordulhat, hogy csak egyszer szeretnénk használni, még nevet sem szeretnénk neki adni. Az ilyen célra hozták létre a Java nyelv megalkotói a név nélküli osztályokat (anonymous class): segítségével pl. egy absztrakt osztályt nem kell példányosítani, hanem elég "röptében" (on the fly) megvalósítani az absztrakt függvényeket.

Vegyük a fenti példát! Az AbstractClass-t hagyjuk meg, a ConcreteClass-t töröljük ki, és írjuk át a példányosítást úgy, hogy ne kelljen megvalósítani nevesítve az AbstractClass-t. A megoldás az alábbi:

public class Main {
    public static void main(String[] args) {
        AbstractClass abstractExample = new AbstractClass() {
            @Override
            public int multiply(int a, int b) {
                return a * b;
            }
        };
        System.out.println(abstractExample.multiply(2, 3));
    }
}

A final kulcsszó

Az objektumorientált programozás egyik lényege az, hogy módosítani, kiterjeszteni tudjuk a meglevő kódot anélkül, hogy ahhoz hozzányúlnánk. Vannak viszont esetek, amikor ezt kifejezetten meg szeretnénk tiltani, ilyen-olyan oknál fogva. A final kulcsszót tudjuk használni erre a célra, a következő esetekben:

  • Osztályok: ezeket az osztályokat nem lehet kiterjeszteni. Szintaxis: public final class MyFinalClass. Az abstract és a final természetesen kizárják egymást.
  • Függvények: a final kulcsszóval ellátott függvényt nem lehet felüldefiniálni a leszármazott osztályban.
  • Változók: a final kulacsszóval ellátott változóknak nem tudunk új értéket adni, így gyakorlatilag azok konstansok. Mivel nem lehet neki utólag más értéket adni, így azonnal értéket kell neki adni. Ez alól egyetlen kivétel van: a final attribútumok kaphatnak értéket a konstruktorban.

Statikus import

A fenti importálás szakaszban nem fejtettem ki a lehetőségek minden szegletét: a Java 1.5-ös verziójától kezdve bevezetésre került a statikus import. Enélkül a statikus függvényeket csak osztálynévvel tudtuk meghívni; a statikus importtal osztályban levő függvényre hivatkozhatunk. Leggyakrabban az egységtesztelésnél használjuk, ahol a különböző assert függvények statikusok, és egyszerűbb és átláthatóbb lesz a kód, ha a fejlécben van egy statikus import; nagy általánosságban viszont inkább célszerű ezt a gyakorlatot elkerülni kerülni.

Az alábbi példa a szintaxist mutatja meg. A bonyolultabb matematikai műveletek a java.lang.Math osztályban szerepelnek statikus függvényekként; a példa azt mutatja be, hogy a hatványozást hogyan tudjuk végrehajtani anélkül, hogy ki kellene írnunk a Math. előtagot.

import static java.lang.Math.pow;
 
public class Main {
    public static void main(String[] args) {
        System.out.println(Math.pow(2, 3));
        System.out.println(pow(2, 3));
    }
}

Interfész alapértelmezett megvalósítással

A Java 8-ban megjelent az a lehetőség, hogy egy interfészen belül egy metódusnak alapértelmezett megvalósítást adhatunk. Ezt a default kulcsszóval tudjuk megtenni (ez a metódus visszatérési típusa elé kerül), és ez esetben adhatunk megvalósítást.

Nem jó gyakorlat interfész metódusokat megvalósítani interfész szinten; az oka annak, hogy bevezették az az, hogy ha van egy létező interfész, amit számos osztály megvalósít, és az interfészbe bele szeretnénk tenni egy új metódust, amire esetleg a legtöbb megvalósító osztálynak nincs is szüksége, akkor ne kényszerítsük az összes osztályt arra, hogy számára felesleges kóddal szemetelje össze magát. Ehelyett adjunk meg egy triviális megvalósítást, ami pl. kiírja, hogy ez még nincs megvalósítva, vagy ad egy triviális megvalósítást.

Felmerül a kérdés, hogy ha osztályokban lehet megvalósítás nélküli (absztrakt) metódusokat létrehozni, interfészekben pedig tudunk alapértelmezett megvalósítást adni, akkor mi a különbség az osztály és interfész között, és melyiket használjuk. Az osztály és interfész között elég sok különbség van, de ami a legfontosabb: amíg az osztályon belül absztrakt függvények létezése az objektumorientált programozás lényegét képezik, addig az alapértelmezett megvalósítással rendelkező interfészek kerülendők.

Unless otherwise stated, the content of this page is licensed under Creative Commons Attribution-ShareAlike 3.0 License