Volledige versie bekijken : [JAVA] Info over abstracte klassen en interfaces



Toelly
15 January 2010, 16:45
Zoals de titel al zegt, heb ik het een beetje moeilijk met het verschil te zien tussen abstracte klassen en interfaces. Ik weet wel wat een abstracte klassen is en wat een interface is, maar ik snap niet echt wat het verschil in functie is binnen het programma, evenals het nut van een abstracte klasse en een interface.

Groeten,
Toelly :)

ultddave
15 January 2010, 17:29
In C++ zijn bepaalde functies van die klasse virtual. (Zodat je die functies kan 'overriden' in afgeleide klassen ervan).

Dus in je abstracte klasse heb je een virtual/abstract functie genaamd "test()" bijvoorbeeld. En dan heb je een klasse "ultddave" en die klasse erft over van die abstracte klasse. En in "ultddave" heb je ook een functie genaamd "test()".

Als je dan "test()" gaat uitvoeren op die abstracte klasse (via polymorfisme), gaat eigenlijk "test()" in ultddave uitgevoerd worden.

Als je inheritance (overerving) en polymorfisme hebt gezien dan zal hopelijk bovenstaande uitleg volstaan. ;)

http://java.sun.com/docs/books/tutorial/java/IandI/abstract.html

Interfaces zullen wel pure virtual klassen zijn. Dat zijn abstracte klassen waarbij geen method body geimplementeerd is, en die klasse bevat dus eigenlijk geen eigen methodes. Alle functies/methods zijn virtual en kunnen dus overriden worden in de afgeleide klasse. Door enkel bepaalde functies toe te laten, kan je dus een protocol maken. ;)

Een interface is dus een 'nog abstractere' versie van een abstract klasse. Aangezien de abstracte klasse nog een paar eigen functies zal hebben. Maar een interface heeft enkel abstracte / virtual functies en methods. ;)

Het doel is dus meestal voor restricties, regels of protocollen te maken. Opzich ga je abstracte klassen wel regelmatig nodig hebben als je veel met overerving werkt. Interfaces bijna niet.

Maar ik programmeer zelden in Java, dus kan zijn dat er ergens een fout zit ;D. Maar volgens de uitleg van wiki is dat hetzelfde als een klasse met virtual functies.
http://en.wikipedia.org/wiki/Abstract_type
http://java.sun.com/docs/books/tutorial/java/IandI/abstract.html

Mvg,
Dave

Toelly
15 January 2010, 18:11
Je uitleg klopt volgens mij ook wel, heel erg bedankt daarvoor :good:

Maar wat als je nu naast de klasse "ultddave" ook de klasse "toelly" hebt, met ook diezelfde functie "test()". Wanneer je dan op die abstracte klasse "test()" uitvoert, dan weet die toch niet of de "test()" uit "ultddave" of uit "toelly" moet gebruikt worden?

En in het algemeen, wat is het nut van abstracte klassen en interfaces? In jouw voorbeeld herschrijf je de functie "test()" toch helemaal uit in "ultddave". Waarom moet je die dan nog eens (onnodig?) typen in een abstracte klasse?

mvg
Toelly

Martijnc
15 January 2010, 18:36
Een abstracte klasse kan niet geïnstantieerd worden, je moet hem eerst extenden. Een abstracte klasse bevat meestal nog methods die niet gedefinieerd zijn (geen body). Je kan dan een nieuwe klasse definiëren en daarmee de abstracte klasse extenden (overerving), je moet dan minstens alle abstracte methods (zonder body in de abstracte klasse) definiëren in de nieuwe klasse.
Dit is handig als bepaalde klasse ongeveer hetzelfde doen, je kan dan de methods die voor alle klasse hetzelfde zijn al in de abstracte klasse definiëren en de methods die dan hun 'eigen ding' doen abstract laten. Je moet deze methods dan pas definiëren als je de klasse extend. Het enige verschil tussen dit en gewoon overerven (extending) is dat een abstracte klasse niet gebruikt kan worden (kan je niet instancieren), je moet hem eerst extenden en alle methods definiëren. Het voordeel is dat je de gemeenschappelijke delen niet telkens opnieuw hoeft te schrijven.

Een interface dient om een bepaalde 'functionaliteit' te definiëren, voor deze functionaliteit zijn bepaalde methods (en properties) nodig. Zo kan je verschillen de objecten, die voor totaal andere dingen dienen toch op dezelfde manier gebruiken. In Java bestaan er bijvoorbeeld interfaces om objecten te sorteren, deze interface bevat bepaalde functies bv. de method compareTo: als je nu een array of collectie van dit object wil sorteren zal java kijken of een object groter is als de andere door de compareTo method aan te roepen. Maar we kunnen niet alle objecten op dezelfde manier vergelijken:

Als we integers vergelijken is 10 groter als 2, als dit nu Strings zijn is het net omgekeerd, dit kunnen we dan bepalen in de compareTo method. Dus dezelfde functionaliteit, voor twee totaal verschillende manieren op dezelfde manier geïmplementeerd met een Interface.


Maar wat als je nu naast de klasse "ultddave" ook de klasse "toelly" hebt, met ook diezelfde functie "test()". Wanneer je dan op die abstracte klasse "test()" uitvoert, dan weet die toch niet of de "test()" uit "ultddave" of uit "toelly" moet gebruikt worden?

Je kan de method test niet aanroepen op de abstracte klasse, je kan hem immers niet instanciëren, je kan wel de klasse ultdave en toelly de abstractie klasse laten extenden. Dan kan je objecten van ultdave en toelly aanmaken en de methods daar aanroepen (als je de method test op ultdave aanroept zal die method van ultdave aangeroepen, idem voor toelly).

Toelly
15 January 2010, 18:53
Bedankt Martijnc, ik ben er nu al een stuk wijzer uit geworden :good:

Wat ik nu echter nog altijd niet snap is wat het echte nut van interfaces en abstracte klassen is. In een abstracte klasse bijvoorbeeld definieer je een aantal methodes, maar niks concreet. Daarna extend je een andere klasse met deze abstracte klasse, maar je moet nog altijd alle methodes intypen? Dat levert dan toch niks van winst op?

Martijnc
15 January 2010, 18:59
Je kan in een abstracte klasse bepaalde methods al definiëren, en andere nog niet (alleen de functie definitie, zonder body), alle klasse die deze abstracte klasse extenden moeten de abstracte methods uit de abstracte klasse definiëren. (er kunnen uiteraard nog abstracte methods zijn hierna maar dan is de klasse nog steeds abstract, een klasse is abstract zolang er nog abstracte members inzitten).

Als je dus twee klasse hebt die voor 90% net hetzelfde doen, kan je die 90% in een abstracte klasse definiëren, de overige 10% laat je abstract, daarna extend je deze twee keer en in deze twee nieuwe klasse definieer je de overige 10% maar dan in beide klasse op een ander manier, het voordeel is dus dat je die 90% gemeenschappelijke functionaliteit niet twee keer moet maken (en bij fouten niet twee keer moet aanpassen).

Toelly
15 January 2010, 19:05
Dus je kan in je abstracte klasse je methoden wel volledig definiëren? Of kan je enkel je niet-abstracte methoden in je klasse volledig definiëren en abstracte niet?

Martijnc
15 January 2010, 19:09
Dus je kan in je abstracte klasse je methoden wel volledig definiëren? Of kan je enkel je niet-abstracte methoden in je klasse volledig definiëren en abstracte niet?

Je kan in een abstracte klasse methods inderdaad volledige definiëren (met body, code tussen de {} ) en je kan methods abstract maken (zonder body).

Een gewone klasse (niet abstract) kan nooit abstracte methods bevatten, vanaf het moment dat er 1 method in een klasse abstract is MOET de klasse ook abstract zijn. Je kan dus een klasse hebben met 1000 methods die volledig gedefiniëerd zijn en daar 1 abstracte bij, dan moet de klasse abstract zijn.

Toelly
15 January 2010, 19:15
Maar wat is dan het nut van een abstracte methode? Overal waar die voorkomt in een subklasse (ge-extend met de abstracte klasse) moet je die methode toch nog helemaal definiëren. Daar zit dus toch helemaal geen winst in?

Martijnc
15 January 2010, 19:35
Je kan die abstracte klasse door verschillende klasse laten extenden en die method telkens op een andere manier laten definiëren, als je die abstracte method weg laat uit de abstracte klasse is het geen abstracte klasse meer en kan je hem gewoon gebruiken, maar het probleem is dat die klasse dan nog niet helemaal klaar is. De abstracte method MOET door klasse die de abstracte klasse willen extenden gedefiniëerd worden.

Het is moeilijk om alle functionaliteit van Object geörienteerd programmeren te begrijpen door uitleg te lezen, je moet alles zelf eens proberen en gebruiken, na een tijd zie je vanzelf de verschillen en de voor-en nadelen van al de verschillende technieken.

Hopelijk begrijp je het met die voorbeeld:
We hebben 1 abstracte klasse die we 'gegevens' noemen, we kunnen onze gegevens echter op verschillende manieren opslaan, in een database en in een bestand.
We extenden dus onze 'gegevens'-klasse en krijgen twee nieuwe klasse: 'gegevensDB' en 'gegevensBestand'. Deze klasse doen hetzelfde (ze leveren ons gegevens die we nodig hebben in ons programma, maar ze doen dit op een verschillende manier).

In onze abstracte klasse hebben we een array, en een aantal get-methods, deze zijn voor beide hetzelfde (we hebben immers dezelfde gegevens). Daarna maken we nog een abstracte method leesGegevens(), deze is voor beide anders, de ene leest zijn gegevens uit een database, de andere uit een bestand.

In onze twee 'final' klasse definiëren we nu de method leesGegevens(). Nu lijkt het gebruik van de abstracte method nog zinloos maar kijk:
In ons programma kunnen we nu het volgende doen:



gegevens mijnGegevens; // we hebben hier een variabelen gemaakt van het type mijnGegevens, we hebben de klasse NIET geinstancieerd!

// Nu kunnen we verder in de code bepalen of we gegevens uit de database willen of we deze gegevens uit het bestand willen:
// Als we gegevens uit de database willen doen we dit:
mijnGegevens = new gegevensDB( );
// Willen we de gegevens uit het bestand dan doen we dit
mijnGegevens = new gegevensBestand( );

// Nu willen we de gegevens gaan inlezen, we gebruiken de method leesGegevens( ) hiervoor, nu hoeven wij ons niet meer aan te trekken van waar de gegevens komen, we roepen gewoon de method leesGegevens( ) aan, als we eerder gekozen hebben voor gegevensBestand zal de method uit gegevensBestand aangeroepen worden en worden de gegevens uit het bestand gehaald, idem voor de database.
mijnGegevens.leesGegevens( );

// Hier kunnen we met de gemeenschappelijke get-methods gegevens uit onze klasse halen.
// Dit mag omdat gegevenDB en gegevensBestand child-klasse zijn van gegevens.


We doen hier dus hetzelfde, maar op twee verschillende manieren zonder dat we er veel rekening mee hebben moeten houden.

Toelly
15 January 2010, 20:05
Ach zo, nu denk ik dat ik het ongeveer snap. Bedankt, je voorbeeld heeft echt geholpen :good:
Maar je hebt wel gelijk, het komt erop aan om het veel te oefenen en te gebruiken (wat ik nu dus ook ga doen :D)

ultddave
15 January 2010, 22:13
Nog een voorbeeld waar het gebruikt kan worden. Ik heb het onlangs moeten gebruiken (Wel in C++):

Ik moest een schaakspel maken. Nu, hoe zou jij de klassen maken voor de schaakstukken? ;) Op dit moment zou jij waarschijnlijk dit doen:
- Voor elk soort schaakstuk een klasse maken (voor een paard, pion, loper, koning, ...)

Ok, stel dat je dat zo doet. Nu moet je een klasse "spelbord" gaan maken. En in die klasse zit dus een 2 dimensionale array dat het spelbord/speelveld voorstelt.

Nu komt uiteraard het probleem. Welk type moet die array zijn? Je kan moeilijk een array van Koningen of pionen maken? Want je zit met meerdere schaakstukken... En de schaakstukken moeten kunnen bewegen op het veld, dus het type van elk veld ligt nooit vast.

Dju. Wat nu? :D

Hier komt dus polymorfisme en dus het gebruik van abstract klassen goed van pas. Je moet dus een extra klasse maken, genaamd "schaakstuk". In deze klasse komt alle gemeenschappelijke informatie dat dus alle soorten schaakstukken (paard, pion, ...) gemeen hebben.

Bijvoorbeeld de variabelen/attributen: Positie en kleur. (Om maar snel iets te zeggen)

Specifieke informatie van elk schaakstuk. Bijvoorbeeld, hoever een Pion mag bewegen en dergelijke, wordt opgeslagen in de klasse Pion zelf. Pion en de andere schaakstukken moeten dan "overerven" van de klasse "schaakstuk". Wat logisch is, want ze zijn allemaal schaakstukken.

Wat hebben we dan tot nu toe? We hebben een 1 base class (schaakstuk) en een aantal afgeleide klassen (paard, pion, toren, koning, koningin, loper).

Nu om het probleem met het veld op te lossen. Het veld moet dan van het type "schaakstuk" zijn. (Dus een array van 64 schaakstukken). En dan maak je bijvoorbeeld een koning aan door deze actie:


schaakstuk MijnSchaakstuk;
MijnSchaakstuk = new Koning();

Dus nu heb je een koning. Dat dus een schaakstuk is. Die koning kan je nu op eender welk veld plaatsen. Zonder problemen te hebben met types. ;)
Idem voor alle andere stukken uiteraard.

PS: Uiteraard gaat "schaakstuk" ook abstract functies hebben, die ook in Koning, Loper, ... bestaan. Waardoor dat "MijnSchaakstuk".beweeg() bijvoorbeeld de respectievelijke functie oproept voor de juiste klasse.

Als MijnSchaakstuk een Koning is, gaat hij "beweeg()" van Koning oproepen. Als hij een Pion is, zal hij die van Pion oproepen. Als de klasse geen "beweeg()" functie zou hebben, dan zal hij de functie in "schaakstuk" zelf oproepen (indien gedefinieerd). Tenzij die volledig abstract is, en dus geen body heeft. ;)

Mvg,
Dave

Toelly
15 January 2010, 23:48
Bedankt :good: Echt een supervoorbeeld. Ik ga er morgen zeker ook een paar proberen te maken. In ieder geval al bedankt Martijnc en ultddave voor jullie hulp. Ik zie nu alles al veel helderder. Indien ik nog vragen heb, zal ik ze hier zeker posten ;)

PS @ ultddave: Zit jij toevallig op de univ gent in de eerste bachelor informatica? (Gezien je opdracht van een schaakspel ;))

Pjj
16 January 2010, 00:43
Dave, ik denk dat ge mij zojuist ietske meer OOP hebt doen verstaan.

Heeft iemand toevallig nog documentatie staan over OOP in het algemeen (dus niet toegepast op een bepaalde taal)?

Ook documentatie over design patterns is welkom :)

Dank ;)

Martijnc
16 January 2010, 00:49
Als je informatie wilt over design patterns kan ik je dit boek aanraden:
http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612

ultddave
16 January 2010, 02:35
2e bachelor ICT op de universiteit hasselt. Dat schaakspel was een klein project (duurde maar 2 weken en ging niet op punten) voor te leren werken met die abstracte klassen. ;)

In het eerste jaar zien wij: Java, C, Perl, PHP, XHTML, CSS, JavaScript, MySQL, Ajax, Visual Basic (basis), Linux & Windows scripting (voor de terminal/commandline), ProLog (programming in Logic) das een 5GL taal, Assembler, XML, Glade & GTK (voor GUI's)

In het tweedde jaar zien we dan: Java (OO programming & multithreaded programming), C++ (OO programming & multithreaded programming), openGL, SQL en gaan we verder met Ajax, Java Server Pages en dergelijke

Nu heb ik een trimesteroverschrijdend project, over het bordspel "Kolonisten van Catan". :D

***

Een belangrijke tip voor OO programmeren lijkt me dat ge eerst goed moet kijken wat uw programma moet doen. En dat ge dan pas uw datastructuren en klassen gaat aanmaken. En dus niet direct vanop het zicht, direct klassen gaat aanmaken die ge denkt nodig te hebben. ;)

Bijvoorbeeld, stel dat ge een 'kaartspel' moet maken. Is het nodig om een klasse 'kaart' te maken? Opzich kunt ge gewoon de kaarten nummeren van 1-13 (Aas = 1, 13 = Heer) afhankelijk van het speltype, kunt ge ook 2-14 als Aas het hoogste is. En dan gewoon met Integers werken en dan de nummers vergelijken.

int kaart1 = 5;
int kaart2 = 12;

if(kaart1 > kaart2)
printf("Kaart 1 is beter");
else if(kaart1 < kaart2)
printf("Kaart 2 is beter");

(Ge kunt dan ook enumeration gebruiken om alles duidelijker te maken. Of constanten. Dat ge zegt dat bijvoorbeeld de Dame = 12. En dat ge dan zegt:
int kaart 2 = DAME;

Das iets duidelijker dan "12". :D)

Dus opzich moet ge nooit te snel klassen gaan aanmaken die logisch lijken. Een kaartspel zonder klasse "kaart" lijkt niet logisch, maar het gaat makkelijk. ;)

Uiteraard moet ge dat zien van situatie tot situatie. Want uiteindelijk, als ge met een GUI werkt. Moet ge misschien toch ergens een 'kaart' klasse gebruiken want elke kaart moet zijn texture nog hebben en misschien nog andere eigenschappen waar ik niet direct aan denk.

***

En vrij belangrijk, waar leerkrachten meestal op letten, is dat ge moet zorgen dat elke klasse doet wat hij moet doen. Niet meer, niet minder. Dus uw "schaakspel" (main klasse) moet dus niet vertellen tegen een Pion van "Gij zijt een Pion, dus kunt ge maar 1 vak vooruit gaan". Maar die moet wel zeggen van "Yo Pion, ga naar vak A7". En de "Pion" weet van zichzelf dat hij een Pion is, en dat hij maar 1 vak kan bewegen. Dus die Pion voert die actie dan uit, of returned dat da niet mogelijk is.

Want ik had eerst bijna alles in men "main klasse" gezet :P. Da was dus nogal verkeerd :D.

Maar ge ziet direct als het fout loopt, dan begint ge meestal functies te implementeren met een grote "select case" (switch) of lange If Else structuren.

Zo had ik eerst:

If(Stuk == "Koning")
// Doe X
else if(Stuk == "Koningin")
// Doe Y
else if(...)
...

Nu als ge da ziet staan, is da meestal ni echt goe :P. Dan kunt ge dus beter met zo een "schaakstuk" klasse werken zoals ik al zei.
Dan doet ge dus:
Stuk.Action();

En dan zorgt ge gewoon dat die functie "Action()" abstract is, en dan zal de juiste functie in ofwel Koning of Koningin of ... aangeroepen worden. En da is eigenlijk maar 1 regel ipv die hele If Else structuur :P.

***

Das zowat het belangrijkste dat ik tot nu toe heb ondervonden :D.

Greetz,
Dave

Fck_
16 January 2010, 03:17
Jou richting ziet er echt hoogst interessant uit Dave :o !

carl
16 January 2010, 09:47
De reden om die grote selectiestructuren aan te passen is uitbreidbaarheid en aanpasbaarheid he ;) Stel dat er ooit een nieuw stuk komt dat 3 vakken links of rechts kan bewegen ? (niet waarschijnlijk, maar is een stom voorbeeld) Dan moet je gewoon de interface schaakstuk implementeren in een nieuwe klasse voor dat stuk, en misschien 1 of 2 regeltjes aanpassen elders.
Zoniet moet je een hele case structuur gaan toevoegen enz.

War je daar zegt over nadenken voor je programmeert (= ontwerpen) klopt helemaal :D Als je dat nog niet gezien hebt voel je dat wel mooi aan.
Ik zie jammer genoeg een pak minder talen :( In het eerste jaar was dat java, html, javascript, beetje SQL en dit semester kwam daar juist XML en xobol bij :(

Toelly
16 January 2010, 09:54
In het eerste jaar zien wij: Java, C, Perl, PHP, XHTML, CSS, JavaScript, MySQL, Ajax, Visual Basic (basis), Linux & Windows scripting (voor de terminal/commandline), ProLog (programming in Logic) das een 5GL taal, Assembler, XML, Glade & GTK (voor GUI's)

In het tweedde jaar zien we dan: Java (OO programming & multithreaded programming), C++ (OO programming & multithreaded programming), openGL, SQL en gaan we verder met Ajax, Java Server Pages en dergelijke


Amai da's veel :O
Maar het ziet er wel überinteressant uit. Misschien had ik toch beter voor informatica gekozen :/


@Carl: ook student informatica? :p

carl
16 January 2010, 09:59
Toegepaste informatica (profesionele bachelor @ hogent) ;)

Fck_
16 January 2010, 10:26
Ik ook Toegepaste Informatica (KHLeuven) , en ik zie netdezelfde talen als Carl.
Daarom dat Dave zijn richting er al zo heel interessant uit zag !!

Pjj
16 January 2010, 13:02
Dave ik denk datk u eens op MSN ga vastklampen als ge dat niet erg vindt :D

Toelly
16 January 2010, 13:28
Toegepaste informatica (profesionele bachelor @ hogent) ;)

Ik ken daar een paar mensen uit de 1e bachelor ;)