[java] Cast probleem

Pagina: 1
Acties:

  • Parasietje
  • Registratie: Juli 2004
  • Laatst online: 10-06-2024
Eerst en vooral: excuses voor de topic-titel.

Dit is mijn probleem: ik heb een interface Data, met een aantal klasses die dit implementeren. Nu krijg ik in een stuk code een array van Data-objecten aangeleverd. Ik wil elk van die Data-objecten naar een string converteren.

Nu heb ik een aantal statische methodes gemaakt in Parser:
* parseData(Text text)
* parseData(Image image)
enzovoort, voor elke klasse die Data implementeert heb ik zo'n methode.

Nu doe ik in een methode parseALotOfData(ArrayList<Data> list) het volgende:
Parser.parseData( listItem.getClass().cast(listItem) );

Omdat er voor elke klasse die Data implementeren, een methode parseData bestaat; zou dit runtime nooit verkeerd mogen lopen. Toch geeft javac de volgende error:
code:
1
2
The method parseData(Image) in the type Parser is not 
 applicable for the arguments (capture-of ? extends Data)


Wie geeft met de oplossing? Ik kan eventueel de volgende code gebruiken, maar dat is niet zo proper:
Parser.class.getMethod("parseData", listItem.getClass() ).invoke(listItem);

WebDAV in Vista is horribly broken. Ik wil het fixen, maar ben nog steeds op zoek naar de tarball met de source...


  • Janoz
  • Registratie: Oktober 2000
  • Laatst online: 14:25

Janoz

Moderator Devschuur®

!litemod

Tijdens compileren moet bekend zijn welke class het is. Je zult er dan een (Image) of (Text) voor moeten zetten als explicite cast.

Wat je zou kunnen doen is naast die verschillende parseData methoden ook een parseData(Data data) methode bouwen die met behulp van instanceof kijkt welk type de class is en vervolgens de juiste methode inclusief explicite cast aanroept.

Java:
1
2
3
4
5
public void parseData(Data data){
  if (data instanceof Text){
    parseData((Text)data);
  } else ....
}

Ken Thompson's famous line from V6 UNIX is equaly applicable to this post:
'You are not expected to understand this'


  • PhysicsRules
  • Registratie: Februari 2002
  • Laatst online: 31-03 07:26

PhysicsRules

Dux: Linux voor Eenden

Waarom implementeer je niet per subclasse een Parse method? Dan heb je ook geen problemen met het bepalen van het type.
Java:
1
2
3
4
5
6
7
8
public class Image implements Data
{
    public string parse()
   {
     ...
     return geparsteversievanmij;
   }
}

Door parse in de interface op te nemen weet je ook zeker dat er niet stiekum toch een subclasse is waarvoor geen parse method bestaat.

Eventueel kun je daarnaast dan nog kiezen voor een constructie in Parser
Java:
1
2
3
4
public string parseData(Data data)
{ 
   return data.parse();
}


edit:

Standeman :w

[ Voor 18% gewijzigd door PhysicsRules op 29-11-2006 15:15 ]


  • Standeman
  • Registratie: November 2000
  • Nu online

Standeman

Prutser 1e klasse

Eigenlijk weet alleen de implementatie hoe hij geparst moet worden naar een String. Dus waarom neem je geen parse method op in je interface en implementeer je dit in je implementaties.

Zoiets:
Java:
1
2
3
4
5
6
7
8
9
10
11
public interface Data {
  ....methods....
  public String parse();
}

public DataImplementation {
  ....methods....
  public String parse() {
    //parse spul enzo
  }
}


is imo de netste methode.

edit:

grrrr... physicsrules is eerder }:| :P

@coldstone.. en ik dacht dat ik spuit 11 was ;)

[ Voor 11% gewijzigd door Standeman op 29-11-2006 15:34 ]

The ships hung in the sky in much the same way that bricks don’t.


  • ColdSTone|IA
  • Registratie: December 2002
  • Laatst online: 28-12-2017

ColdSTone|IA

lui..

Kun je niet een parseData() methode toevoegen aan je Data interface? Dan kun je gewoon over je ArrayList itereren en bij ieder element parseData() aanroepen, hoef je niks te casten.
Verder zou je ArrayList<? extends Data> kunnen gebruiken, dan mag je ook sub-klassen (en klassen die een interface implementen) van Data in je ArrayList gooien.

[ Voor 0% gewijzigd door ColdSTone|IA op 29-11-2006 15:23 . Reden: En ik ben natuurlijk weer de overtreffende trap, laatst... ]


  • Parasietje
  • Registratie: Juli 2004
  • Laatst online: 10-06-2024
Het is een dynamische site die ik programmeer. Ik wil kostte wat kost data, logica en lay-out van elkaar scheiden. Daarom mag er geen parse() methode in mijn Data-klassen zitten.
De clue is ook, dat je vrij moet zijn om voor bepaalde klassen _GEEN_ parseData() methode te maken. Dan geeft de templating engine "Datatype not supported" terug.

WebDAV in Vista is horribly broken. Ik wil het fixen, maar ben nog steeds op zoek naar de tarball met de source...


  • Janoz
  • Registratie: Oktober 2000
  • Laatst online: 14:25

Janoz

Moderator Devschuur®

!litemod

In mijn implementatie kun je dat keurig aangeven wanneer de parse(Data data) geen bekende instance tegengekomen is.

Belangrijkste reden voor deze post is echter de title change. Moeilijk omschrijvbaar probleem lijkt me toch duidelijk een cast probleem ;)

Ken Thompson's famous line from V6 UNIX is equaly applicable to this post:
'You are not expected to understand this'


  • Standeman
  • Registratie: November 2000
  • Nu online

Standeman

Prutser 1e klasse

Parasietje schreef op woensdag 29 november 2006 @ 15:44:
Het is een dynamische site die ik programmeer. Ik wil kostte wat kost data, logica en lay-out van elkaar scheiden. Daarom mag er geen parse() methode in mijn Data-klassen zitten.
De clue is ook, dat je vrij moet zijn om voor bepaalde klassen _GEEN_ parseData() methode te maken. Dan geeft de templating engine "Datatype not supported" terug.
Ik denk dat je scheiding van data / BL (business layer) toch niet helemaal correct is. In je domain objecten (wat de implementatie van je Data interface eigenlijk moet zijn) is het geheel correct om parse BL te implementeren.
Meestal krijg je van je persistency layer een DTO (data transfer object) terug waarmee je je domain object instantieert. In dit domain object (welke een implementatie is van de Data Interface) kan je dan de parse() method opnemen. Maar mogelijk gaat dit te ver voor jou oplossing.

Voor de clue is het makkelijk. Gewoon "Datatype not supported" retourneren met de parse() methode vanuit de implementatie objecten (maar goed, mijn (en physicsrules's) oplossing is mogelijk geen oplossing voor jou).

Ik heb overigens een donkerbruin vermoeden dat de implementatie van je Data Interface eigenlijk je DTO object is die je in de BL gebruikt en dat is niet de "mooiste" oplossing.

The ships hung in the sky in much the same way that bricks don’t.


  • Janoz
  • Registratie: Oktober 2000
  • Laatst online: 14:25

Janoz

Moderator Devschuur®

!litemod

Dat ligt er helemaal aan wat de parse methode eigenlijk doet. Aangezien de topicstarter het over een template systeem heeft en er een text en een image geparsed worden vermoed ik een iets slechte naamgeving van een methode die het domein object omzet naar de maneir waarop het afgebeeld moet worden. Zoiets is duidelijk een view actie en hoort ook in de view. Er is echter te weinig informatie om echt uitsluitsel te geven en ook mijn interpretatie barst van de aannames.

Ik ben het echter wel eens met dat de hier aangedragen oplossing niet helemaal netjes is. Static methoden die gaan parsen? Een data interface? Een interface is voor mij gevoelsmatig vaak iets wat iets kan doen terwijl het hier eerder iets is wat iets is. Parsable zou ik in deze beperkte context een passendere interface naam vinden.

Ken Thompson's famous line from V6 UNIX is equaly applicable to this post:
'You are not expected to understand this'


  • Standeman
  • Registratie: November 2000
  • Nu online

Standeman

Prutser 1e klasse

Janoz schreef op woensdag 29 november 2006 @ 16:09:
Dat ligt er helemaal aan wat de parse methode eigenlijk doet. Aangezien de topicstarter het over een template systeem heeft en er een text en een image geparsed worden vermoed ik een iets slechte naamgeving van een methode die het domein object omzet naar de maneir waarop het afgebeeld moet worden. Zoiets is duidelijk een view actie en hoort ook in de view. Er is echter te weinig informatie om echt uitsluitsel te geven en ook mijn interpretatie barst van de aannames.

Ik ben het echter wel eens met dat de hier aangedragen oplossing niet helemaal netjes is. Static methoden die gaan parsen? Een data interface? Een interface is voor mij gevoelsmatig vaak iets wat iets kan doen terwijl het hier eerder iets is wat iets is. Parsable zou ik in deze beperkte context een passendere interface naam vinden.
hmmm.. ik zat inderdaad maar in 1 richting te denken. Het parsen zou inderdaad alleen op de view layer van toepassing kunnen zijn :)

The ships hung in the sky in much the same way that bricks don’t.


  • Grub
  • Registratie: Juni 1999
  • Laatst online: 11-02-2024
Hoi,

Ik denk dat jouw probleem wel eens te maken zou kunnen hebben met het feit dat method selection op runtime voor overloaded methods, in tegenstelling tot overridden methods, gebeurt op basis van het reference type en niet van het object type.

Check de volgende demonstratie code:
Java:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public class Test {

    public void doTheBadThing(Animal a) {
        System.out.println("encourage animals to do the bad thing");
        a.haveSex();
    }

    public void doTheBadThing(Monkey m) {
        System.out.println("encourage monkeys to do the bad thing");
        m.haveSex();
    }

    public static void main(String[] args) {
        Test t = new Test();
        Animal a2 = new Monkey();
        t.doTheBadThing(a2);
    }
}

class Animal {
    public void haveSex() {
        System.out.println("general animal having animal sex");
    }
}

class Monkey extends Animal {
    public void haveSex() {
        System.out.println("monkey is having some good monkey sex");
    }
}


Dit resulteert in de volgende output:
code:
1
2
encourage animals to do the bad thing
monkey is having some good monkey sex


Wat je dus ziet is dat de jvm op runtime kijkt naar het reference type voor het bepalen van welke methode er wordt aangeroepen. Terwijl onderhuids het wel een subklasse is en je zou verwachten dat de meer specificieke methode wordt aangeroepen. Omdat alle objecten in je collectie een "Data" referentie type hebben, verwacht ik eigenlijk dat alleen de overloaded methode die een "Data" object accepteerd, wordt aangeroepen. Maar om dat zeker te weten zou ik al je code moeten zien.

Kewl SCJP stuff.... :D

[ Voor 7% gewijzigd door Grub op 29-11-2006 16:24 ]


  • Janoz
  • Registratie: Oktober 2000
  • Laatst online: 14:25

Janoz

Moderator Devschuur®

!litemod

Je beschrijving klopt niet helemaal. Het grote verschil tussen oo en procedureel is dat je bij oo de methoden bij je data hebt. Je blokje data weet dus welke methoden er bij hem horen.

De reden dat eerst de methode met animal aangeroepen wordt komt omdat de compiler ziet dat je binnen de main een animal object hebt.Welke methode je aanroept wordt bepaald door de signature. Dit is de naam en welke parameter daarbij hoort. Jij roept op het object test doTheBadThing aan met als parameter een animal (a2). Dat dit eigenlijk een monkey is 'weet' de compiler niet aangezien a2 van het type animal is. Binnen de methode echter roep je op a de methode haveSex aan. Omdat het meegegeven object, wat van het type monkey is, weet welke methoden bij hem horen, zal dan wel de aap variant aangeroepen worden.

Ken Thompson's famous line from V6 UNIX is equaly applicable to this post:
'You are not expected to understand this'


Verwijderd

Zo'n "dynamische cast" gaat in een statisch gecompileerde taal zoals Java natuurlijk nooit werken zonder enige introspectie-trucs. De enige type informatie die aanwezig is Data en de code in Parser.parseALotOfData() weet niks van Text en Image subclasses en kan de link dus niet leggen.

De instanceof-oplossing van Janoz is de meest simpele en directe en in dit geval ook de meest logische want als je voor elke nieuwe subclass toch al een nieuwe methode moet toevoegen aan de Parser class dan kun je net zo goed ook een nieuwe if(instanceof) toevoegen.

Als je de Data en Parser classes goed gescheiden wilt houden kun je gebruik maken van de strategie (voor GoF-kenners: pun intended) waarbij er voor elke Data implementatie die geparsed kan worden een DataParser object is.

De DataParser interface heeft dan gewoon een parse(Data) methode en bijv. de TextParser weet precies hoe hij een Text in een String kan omzetten.

Blijft over dat de Parser moet weten welke DataParser implementatie bij welk Data object hoort, maar dat is iets dat je óf configureerbaar kunt maken (vie een XML file o.i.d.) óf via een simpele introspection waarbij je dynamisch de class "org.foobar.parsers." + data.getClass().getName() + "Parser" probeert te laden of zo. (Persoonlijk zou ik voor de configuratie file kiezen).

  • Grub
  • Registratie: Juni 1999
  • Laatst online: 11-02-2024
Janoz schreef op woensdag 29 november 2006 @ 16:32:
...Je beschrijving klopt niet helemaal...
Ja eh volgens mij zeg jij met andere woorden precies hetzelfde als ik dus wat klopt er niet aan wat ik zeg?

  • prototype
  • Registratie: Juni 2001
  • Niet online

prototype

Cheer Bear

Als je dan echt geen parse methode in je data wil hebben, kun je denken aan visitor pattern gebruiken.

Visitable.java
Java:
1
2
3
4
5
package got;

public interface Visitable {
    public void accept(Visitor v);
}


Visitor.java
Java:
1
2
3
4
5
6
package got;

public interface Visitor {
    public void visit(Text t);
    public void visit(Image i);
}


Data.java
Java:
1
2
3
4
package got;

public interface Data extends Visitable {
}


Image.java
Java:
1
2
3
4
5
6
7
8
package got;

public class Image implements Data {
    public void accept(Visitor v)
    {
        v.visit(this);
    }
}


Text.java
Java:
1
2
3
4
5
6
7
8
package got;

public class Text implements Data {
    public void accept(Visitor v)
    {
        v.visit(this);
    }
}


Parser.java
Java:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
package got;

import java.util.*;

public class Parser implements Visitor {
    public void visit(Text t)
    {
        System.out.println("Text visited");
    }
    
    public void visit(Image i)
    {
        System.out.println("Image visited.");
    }
    
    public void visit(List<? extends Data> l)
    {
        for(Data d : l)
        {
            d.accept(this);
        }
    }
    
    public static void main(String[] args) {
        List<Data> l = new ArrayList<Data>();
        l.add(new Text());
        l.add(new Image());
        
        Parser p = new Parser();
        p.visit(l);
    }
}

edit:
Voor het gemak ben ik er even van uit gegaan dat al je data Visitable is, uiteraard kun je dit makkelijk aanpassen specifiek per concrete classe door Visitable niet Data te laten extenden en gewoon Text of Image b.v. zowel Visitable als Data te laten implementeren.

[ Voor 10% gewijzigd door prototype op 29-11-2006 17:08 ]


  • Janoz
  • Registratie: Oktober 2000
  • Laatst online: 14:25

Janoz

Moderator Devschuur®

!litemod

Grub schreef op woensdag 29 november 2006 @ 16:49:
[...]

Ja eh volgens mij zeg jij met andere woorden precies hetzelfde als ik dus wat klopt er niet aan wat ik zeg?
euhm.. inderdaad verschilt het niet veel. Toen ik begon te typen reageerde ik vooral op het
Wat je dus ziet is dat de jvm op runtime kijkt naar het reference type voor het bepalen van welke methode er wordt aangeroepen.
deel. Voor mijn gevoel moest dat niet runtime, maar compile time zijn. Tijdens het compileren wordt aangegeven dat je de methode met signature "void doBadThing(Animal)" aanroept. Maar nu ik het herlees is het zo'n minimaal onderdeel van je verhaal dat het inderdaad voroal op hetzelfde neerkomt.

Ken Thompson's famous line from V6 UNIX is equaly applicable to this post:
'You are not expected to understand this'


Verwijderd

prototype schreef op woensdag 29 november 2006 @ 16:55:
Als je dan echt geen parse methode in je data wil hebben, kun je denken aan visitor pattern gebruiken.
Ik hoopte bij het lezen van de vraag eigenlijk stiekem dat dit pattern niet voorbij zou komen. Eensch, het performed verdomde goed, maar performance is lang niet alles. 1 Class die feitelijk alle zooi afhandeld (visitor) helpt je nou niet bepaald met scheiden (en misschien hergebruiken) van de juiste formatting logica.

Een alternatief is dan bijvoorbeeld gebruik te maken van een Map. map een class op een of andere formatting class (Map<Class, Formatter>). En nu dat ik er wat langer over nadenk zou je zelfs het standaard PropertyEditor class (of een soortgelijk mechanisme) aan de slag kunnen gaan.

edit:
eigenlijk is het dus een nette encapsulering van wat Janoz eerder heeft voorgesteld.

[ Voor 5% gewijzigd door Verwijderd op 29-11-2006 23:05 ]

Pagina: 1