2016-08-09 35 views
2

Ich versuche, eine Variable Klasse zu erstellen, die entweder einen Integer oder Double Wert mit Generics darstellen kann.Java-Generics mit statischen Factory-Methoden

Unten ist der Code, den ich ausprobiert habe. Wegen der Löschung verwende ich eine enum, um den beabsichtigten Typ des Variable zu speichern und dann zu versuchen, diesen zu verwenden, um den Wert auf den richtigen Typ zu initialisieren.

public class Variable<T> { 

    private enum Type {INTEGER, DOUBLE}; 
    private Type type; 

    private T value; 

    public static Variable<Integer> createAsInteger() { 
     return new Variable<Integer>(Type.INTEGER); 
    } 

    public static Variable<Double> createAsDouble() { 
     return new Variable<Double>(Type.DOUBLE); 
    } 

    private Variable(Type type) { 
     this.type = type; 

     if(type == Type.INTEGER) { 
      value = new Integer(0); 
     } else { 
      value = new Double(0.0); 
     } 
    } 

    public static void main(String[] args) { 
     Variable.createAsInteger(); 
     Variable.createAsDouble(); 
    } 

} 

Allerdings, wenn ich es kompilieren bekomme ich folgende Meldung ...

error: incompatible types: Integer cannot be converted to T 
        value = new Integer(0); 

und ebenfalls für die Double.

Kann jemand erklären, warum dies geschieht und ob es einen Weg gibt, ohne zwei separate Klassen zu schreiben, eine für Integer und eine für Double?

bearbeiten

Vielen Dank für Ihre Antworten ... basierend auf sie, ich weiß jetzt, es gibt bessere Möglichkeiten, dies zu tun. Ich versuche jedoch herauszufinden, warum dieser Ansatz nicht funktioniert, damit ich in Zukunft nicht denselben Fehler mache.

Wenn ich meine Klasse als public class Variable<T extends Number> wie vorgeschlagen definiere, bekomme ich immer noch den gleichen Fehler.

Antwort

0

Sie erhalten den Fehler, weil Sie eine Methode in einer generischen Klasse typisieren. Sie können einige innerhalb der generischen T-Klasse nicht definieren.

Übrigens verwechseln Sie das Designmuster.

Sie müssen eine generische Klasse für Variable entwerfen, auch der Konstruktor muss T als Argumenttyp haben.

In einer anderen Klasse implementieren Sie die Factory mit den Methoden createInteger und createDouble.

2

Ihre Architektur scheint das Konzept der Generika zu verunreinigen.

class Variable<T extends Number> {...} 

Dann können Sie eine generische Fabrikmethode eine Variable<X> basierend auf Ihren gewünschten Klasse erstellt haben:

eine obere Schranke in der Typ-Parameter Die einfachste Art und Weise zu haben wäre

static <X extends Number>Variable<X> create() { 
    return new Variable<X>(); 
} 

Sie können ihn dann wie folgt aufrufen:

Dies ist nicht aufbeschränktund Double, aber eher jede Number.

Wenn Sie müssen, können Sie diese Möglichkeiten begrenzen, indem die folgenden:

  • einen Parameter Ihre create Methode hinzufügen: create(Class<X> clazz)
  • Überprüfen Sie den Wert Ihres clazz Argument innerhalb des Körpers des Verfahrens:

    if (!clazz.equals(Integer.class) && !clazz.equals(Double.class)) { 
        // TODO complain 
    } 
    

Andernfalls können Sie sicherstellen, dass Sie verwenden ein private Konstruktor und static createAs... nicht-generische Methoden zur Verfügung stellen, wie createAsInteger etc., die hier eine new Variable<Integer> usw.

+1

Sie brauchen nicht wirklich 'Class clazz', wenn Sie die Klassenvariable nicht überprüfen werden. 'static Variable create()' funktioniert. –

+0

@AndyTurner du hast natürlich Recht. Der 'clazz'-Parameter ist nur dazu da, um genau zu steuern, was die' Nummer' sein kann oder nicht. Ich werde die Antwort jedoch bearbeiten. – Mena

+0

Ich bin nicht auf die zwei Sätze von Diamantoperatoren in einer Methodenanweisung vorhin gestoßen, d. H. ' Variable '. Kannst du Links empfehlen, wo ich mehr darüber lesen kann? – soarjay

1

Das Problem zurückkehren würde, ist, dass T alles sein kann. Was passiert, wenn T zum Beispiel war String, würde Ihr Code betragen:

String value = new Integer(0); 

Sie könnten Ihre Fabrik Methoden wie diese auslegen:

public static Variable<Integer> createAsInteger() { 
    return new Variable<>(new Integer(0), Type.INTEGER); 
} 

Wo Sie einen Konstruktor haben wie:

private Variable(T value, Type type) { 
    this.value = value; 
    this.type = type; 
} 
0

Sie können Ihre Klasse von Numbers erben lassen und Typprüfungen verwenden, um ap aufzurufen geeignete Methode für Integer oder Double.

public class Variable<T extends Number> { 
    public static Variable<T extends Number> Variable<T> create(Variable<T> var){ 
       if (var instanceOf Integer){ 
       // Take appropriate action 
       } 
       else if (var instanceOf Double){ 
       // Take appropriate action 
       } 

    } 
} 

Dadurch gibt es keine besondere Notwendigkeit, eine separate Enum für Typen zu pflegen.