2017-10-24 2 views
1

Lassen Sie uns sagen, dass ich eine Java-Basisklasse haben:Rückkehr verschiedene Typen von einer Java-Methode

class base { 
    public Class<? extends base> type; 
    // ... 
} 

type speichert den Classtype von Klassen, die von base

erben Es gibt eine andere Klasse, die base Objekte in einem speichert Behälter, es hat auch eine get Methode, die in diesem Container gespeicherten Objekte zurückgibt:

class container { 
    private Vector<base> v; 
    //... 
    public <something here> get(int i){ 
     base b=v.get(i); 
     return b.type.cast(b); 
    } 

Wie implementiere ich eine solche Funktion (container.get), die das Objekt vor der Rückgabe in den richtigen Typ bringt? Es wäre schön, in einem Cascading Style programmieren zu können:

JSONObject o; 
//... 
o.get("nestedObject").get("array").get(3); 
+0

müssen Sie von container.get einen beliebigen Typ zurückgeben, der von der Basis ausgeht? – msoliman

+0

Ja // 10 weitere Zeichen – saga

+0

Wenn ich richtig verstanden habe, können Sie generische mit der Beschränkung von der Basis verwenden, ist das was du meinst? – msoliman

Antwort

1

Sie verstehen möglicherweise, was ein "Cast" in Java ist.Mit Ausnahme von Umwandlungen, bei denen primitive Typen verwendet werden (z. B. das Umwandeln einer int in eine float), ändern die Umwandlungen Objekte überhaupt nicht.

Einer Ihrer Instanzvariablen ist

private Vector<Base> v; 

(Bitte geben Sie die Java-Konvention verwenden und Klassennamen mit Großbuchstaben beginnen. Ich base-Base geändert habe ein gutes Beispiel zu geben.)

Die Objekte, die Sie in diesen Vektor einfügen, können vom Typ Baseoder einer Unterklasse vonBase sein. Der Typ des Objekts ist festgelegt, wenn das Objekt erstellt wird. Angenommen Base hat drei Unterklassen, Child1, Child2 und Child3. Diese Objekte können mit new Child1(...), new Child2(...), new Child3(...) konstruiert werden. Sobald Sie new Child1(...) sagen, haben Sie ein Objekt, dessen Typ Child1 ist, und Sie können nichts tun, um das zu ändern. Aber da eine Child1 eine Base ist, können Sie Ihre Child1 überall verwenden, wo Sie eine Base verwenden können, wie das Einfügen in Ihren Vektor v.

Casting bewirkt nur, dass der Compiler eine Variable anders betrachtet. Angenommen, Sie sagen

Base x = v.get(i); 

x (wenn nicht null) ein Base oder ein Objekt einer Unterklasse sein wird. Der Typ x ist alles, was Sie zum Konstruieren des Objekts verwendet haben. Nun, wenn Sie sagen

Child1 y = (Child1)x; 

Wenn x ein Child1 ist, y wird ein Verweis auf das gleiche Objekt wie x sein. Aber der Compiler weiß, dass y ein Child1 ist, und Sie können auf neue Methoden zugreifen, die in einem Child1 definiert sind. (Wenn x kein Child1 ist, erhalten Sie eine Ausnahme.) Es ist jedoch wichtig zu beachten, dass eine Umwandlung kein neues Objekt erstellt und den Typ von nichts ändert. Es weist den Compiler lediglich an, eine Variable oder einen Ausdruck auf andere Weise zu betrachten.

Vor diesem Hintergrund können Sie nichts gewinnen, wenn Sie versuchen, eine Methode zu schreiben, die einen "variablen" Typ zurückgibt. Sie können auch schreiben Sie einfach

public Base get(int i){ 
    return v.get(i); 
} 

Das Objekt, das es gibt ein Base, Child1, Child2, etc sein könnte .; Es ist der gleiche Typ wie das Objekt, als Sie es in den Vektor eingefügt haben. Wenn Sie get rufen, wenn Sie das Ergebnis erwarten ein Child1 zu sein, können Sie

Child1 child = (Child1)container.get(n); 

sagen, welche eine Ausnahme auslösen, wenn das Objekt kein child1 ist. Oder Sie können instanceof verwenden, um sie zu überprüfen:

Base child = container.get(n); 
if (child instanceof Child1) { 
    ... 
} 

Wenn dies nicht gut genug ist, müssen Sie deutlicher erklären, was Sie erreichen wollen und warum dies nicht funktionieren wird.

MEHR: Nach dem Bearbeiten lesen, das sagt, Sie wollen etwas tun:

JSONObject o; 
//... 
o.get("nestedObject").get("array").get(3); 

Sie brauchen kein Casting, dies zu erreichen. Was Sie benötigen, ist ein abstrakter Basistyp JSONValue, der jede Art von Wert darstellt, der von einem JSON-Parser zurückgegeben werden kann. Die Unterklassen sind Dinge wie "Objekt", "Array" und alle skalaren Typen, die Sie benötigen (Integer, String, etc.). get würde JSONValue zurückgeben. In Wirklichkeit gibt es ein Objekt eines anderen Typs zurück, aber der Compiler muss wissen, dass es JSONValue zurückgibt.

Jetzt ein JSONValue benötigen zwei get Methoden, eine, die eine String und man nimmt, die eine int oder Integer nimmt. Dies werden polymorphe Methoden sein. Sagen Sie v ist ein JSONValue und Sie sagen v.get("array"): die Methode, die es tatsächlich aufruft, kann basierend auf dem tatsächlichen Typ von v anders sein. Wenn also v Ihr "Objekt" -Typ ist, ruft er die Methode auf, die Sie für den Objekttyp definiert haben; Wenn v ein Integer-Typ ist, wird die für den Integer-Typ definierte Methode usw. aufgerufen. Was Sie wollen, ist für die get Methode, die eine String nach dem Feld für den JSON "Objekt" Typ sucht, und eine Ausnahme für alles andere auslösen. In ähnlicher Weise wird für die get-Methode, die einen ganzzahligen Parameter verwendet, die Methode für den JSON- "Array" -Typ eine Liste oder etwas nachschlagen, und die Methode für jede andere Unterklasse von JSONValue löst eine Ausnahme aus. Kein "Casting" ist dafür erforderlich. Casting ist ein Kompilierzeitkonzept. An allen Punkten weiß der Compiler nur, dass er mit einer JSONValue arbeitet. Aber zur Laufzeit wird der tatsächliche Typ des Objekts verwendet, um zu bestimmen, welche Methode aufgerufen werden soll, sodass der Compiler nichts mehr wissen muss.

Dies ist ein Grundkonzept, das alle Java-Programmierer wissen müssen (auch Javascript, Python und so ziemlich jede andere Sprache heutzutage, obwohl Polymorphie in interpretierten Sprachen wie Javascript und Python anders gehandhabt wird). Das Tutorial https://docs.oracle.com/javase/tutorial/java/IandI/index.html deckt dieses und verwandte Konzepte ab. Es könnte auch bessere Java-Tutorials zum Thema "Polymorphismus" geben.

+0

Ja, das wird funktionieren, das ist, was ich gerade mache, aber es ergibt 'if-else' Ketten der Länge 6! Dieser Code wird häufig dupliziert (für jeden Dateneintrag in der JSON-Datei). Ich möchte es irgendwie umgestalten, vorzugsweise in der Bibliothek selbst. – saga

+0

Sie sollten wahrscheinlich nicht so viel 'if-else' brauchen. Wenn Sie ein Beispiel für den Code veröffentlichen, der die große 'if-else'-Kette verwendet, können wir vielleicht eine bessere Lösung finden. Es scheint wahrscheinlich, dass Sie versuchen, das Problem an der falschen Stelle zu lösen. Das heißt, Ihre Frage ist, wie Sie die Bibliothek schreiben, aber es sollte darum gehen, wie Sie den Code schreiben, der sie verwendet. – ajb

-3

Ich glaube, Sie

class container<GenericType> { 
    private Vector<GenericType> v; 
    //... 
    public GenericType get(int i){ 
     GenericType b=v.get(i); 
     return b; 
    } 
} 
+0

In meinem Fall muss 'GenericType'' base' sein, dann liefert die von Ihnen angebotene 'get' Methode immer ein' base' Objekt. Ich möchte, dass der Code das im Container gespeicherte Objekt in den korrekten Typ umwandelt, wie er im Feld 'type' von' base' gespeichert ist. 'Base' und seine Kinder werden im Container aufbewahrt. – saga

0

zu Generics beziehen können Sie nicht tun. Generika funktionieren nicht wirklich so. Es gibt keine Möglichkeit, den genauen Untertyp Ihres Objekts zur Kompilierungszeit zu kennen.

Diese b.type.cast(b); Casting muss auf das Ergebnis Ihrer Methode public <something here> get(int i) getan werden, wo immer es heißt.

Wenn Sie das System gut entwickelt haben, müssen Sie das Objekt nicht mehr, wenn überhaupt, in die exakte Unterklasse zurückverwandeln.

Andernfalls ist Ihre beste Wette, eine instanceof Art zu überprüfen, wo erforderlich.

+0

Ich stellte dieses Problem beim Schreiben eines JSON-Dateiparsers. Ich habe eine Basis-JSontype-Klasse und mehrere andere Klassen (wie Objekt, Array, Double, String ...), die davon ausgehen. JSON-Arrays werden als Arraylist von 'jsontype's und Objekten als' HashMap 'implementiert. Die Benutzer dieses Codes müssen das Objekt manuell umwandeln, um sie zu verwenden. Was könnte ich anders machen, um diese Hässlichkeit zu vermeiden? – saga

+1

Weitere Informationen finden Sie unter https://github.com/google/gson/blob/master/gson/src/main/java/com/google/gson/Gson.java. Sie möchten sich die Methode 'fromJson' anschauen. – Thihara

+0

Danke, ich schaue mal rein – saga

1

type speichert den Classtype von Klassen, die von der Basis erben

Dies entspricht this.getClass().

Wie implementiere ich eine solche Funktion (container.get), die das Objekt auf den korrekten Typ vor der Rückgabe umwandelt?

In der Theorie nicht möglich. Die Benutzer erstellen weitere Unterklassen und fügen sie in den Container ein, nachdem der Code kompiliert und versendet wurde. Das ist der Zweck einer Basisklasse - damit der andere Teil Ihrer Codebasis nur mit einer einzigen Schnittstelle/Konzept arbeiten muss.

Also im Grunde, was Sie schreiben möchten, ist nur einige generische Container-Code.

interface Container<T extends Base> { 
    public T get(int i); 
} 

Aber die eigentliche Frage war Wie kann der Behälter eine Reihe von bekannt Subklassen aufnimmt? Verwenden Sie einfach andere Methodennamen. Gson tut es.

public class Container { 
    public Base get(int i) { ... } 
    public SubType1 getAsSubType1(int i) { ... } 
    public SubType2 getAsSubType2(int i) { ... } 
} 

Und Sie können benutzerdefinierte Methoden zu Base zu insg.

+0

Nur wenn er einen homogenen Behälter haben will. Aber ich sehe das nicht in der Frage. – ajb

+0

'Vektor '? Alles im Container sollte eine Unterklasse von 'Base' sein, oder? @ajb –

+0

Aber die Objekte in einem Container könnten verschiedene Unterklassen von 'Base' sein. Es macht nicht viel Sinn, dies zu einem generischen Container zu machen, wenn die Objekte alle verschiedene Unterklassen von 'Base' sein könnten - Sie verwenden einfach 'Container 'bei jeder Verwendung des Containers. – ajb

Verwandte Themen