2015-06-11 9 views
5

Ich möchte einen typsicheren Code schreiben. Hier ist, was ich versucht habe:Schreiben einer Klasse in einer typsicheren Weise

public interface ResultTronsformer<T>{ 
    public T tranform(T t); 
} 

public class BigDecimalTransformer implements ResultTRansformer<BigDecimal>{ 

    public BigDecimal transform(BigDecimal t){ 
     return t.setScale(0); 
    } 
} 

Jetzt habe ich die Spalte Schnittstelle definieren, die wie

public interface Column{ 
    public ResultTransformer<?> getTransformer(); 
} 

sieht und möchte es in dem Verfahren verwenden

public class Report{ 
    private Map<Column, Object> columnValuePairs; 

    public void putIntoACollection(Column c, Object columnsValue){ 
     ResultTransformer<?> rt = c.getTransformer(); 
     columnValuePairs.put(c, rt.transform(o)); //Error: Couldn't convert Object 
                //to the capture of wildcard 
    } 
} 

Wie kann ich neu ordnen das Design, um die gewünschte Typ-Sicherheit zu erreichen? Vielleicht sollte ich stattdessen zur Laufzeit die Typprüfung durchführen (Ausnahme auslösen)?

+1

Javas Typsystem ist nicht stark genug h, dies zu tun. Sie können jedoch einige Wrapper-Bibliotheken verwenden, die eine ** extern ** sichere API bereitstellen. Zum Beispiel: http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/ClassToInstanceMap.html – Max

+0

@Max Intersting, habe noch nie von diesem Ansatz gehört. Vielleicht können Sie als Beispiel ein Beispiel geben? – user3663882

Antwort

1

Sie können die Column Klasse ändern und machen es parametrisiert:

public interface Column<T> { 
    public ResultTransformer<T> getTransformer(); 
} 

Dann müssen Sie die putIntoACollection Methode parametrisieren (keine Notwendigkeit Report parametrisieren):

public class Report { 
    private Map<Column, Object> columnValuePairs; 

    public <T> void putIntoACollection(Column<T> c, T columnsValue) { 
     final ResultTransformer<T> rt = c.getTransformer(); 
     columnValuePairs.put(c, rt.transform(columnsValue)); 
    } 
} 

Auf diese Weise, die Sie nie müssen einen Erfassungstyp verwenden. Hier

ist ein Beispiel dafür, wie Sie es verwenden würden:

private class BigDecimalColumn implements Column<BigDecimal> { 
    @Override 
    public ResultTransformer<BigDecimal> getTransformer() { 
     return new BigDecimalTransformer(); 
    } 
} 

public static void main(String[] args) { 
    final Report report = new Report(); 
    report.putIntoACollection(new BigDecimalColumn(), new BigDecimal("3.14")); 
} 
+0

Aber wie denkst du, kann ich dann die Column-Schnittstelle implementieren? Zum Beispiel brauche ich 'BigDecimalColumn', was wiederum eine Unterklasse von' ResultTransformer 'ist. Wir werden eine Compiler-Warnung erhalten. – user3663882

+0

Sie haben Recht. Ich habe meine Antwort aktualisiert. –

+0

@ user3663882 diese Frage wird unmöglich in einer Weise gelöst werden, die diese Warnung nicht erzeugt.Das einzige, was zu tun ist, wird beweisbar sicheren Code schreiben und fügen Sie ein '@SuppressWarning (" unchecked ")'. – Max

1

Wenn Sie den Transformator zu erhalten, müssen Sie den Typen angeben, da die Compiler es nicht zu dieser Zeit kennen.

Eine mögliche Lösung besteht darin, die Klasse als Parameter zum getTransformer der Spalte hinzuzufügen und einen spezialisierten ResultTransformer zurückzugeben.

Ein anderer Weg wäre, die Schnittstelle Spalte zu verallgemeinern.

+0

Auch hier ist die Implementierung der Column-Schnittstelle unsicher. Oder ich verstehe etwas nicht ....? – user3663882

+0

Sie könnten auch die Spaltenschnittstelle zu Spalte verallgemeinern, aber das würde die Typparameter der Spalte an das Ergebnis des ResultTransformers binden, was keine Abhängigkeit ist, die ich wählen würde. In der Zukunft möchten Sie vielleicht mehrere Transformatoren für eine Spalte haben und das wird nicht mehr möglich sein. Es hängt davon ab, welche Klasse die Verantwortung trägt, den Rückgabetyp des Transformators zu definieren. Wenn es die Spalte ist, machen Sie Spalte . Wenn es der ResultTransformer ist, dann ist dies ein guter Weg. – Maarten

2

Sie können über die Spalte genau wie über eine Art Container denken, der einen bestimmten Typ enthält. Auf diese Weise können Sie in der Spalte Deklaration einen generischen Typ einführen.

public interface Column<T>{ 
    public ResultTransformer<T> getTransformer(); 
} 

Dann können Sie Bericht Verfahren wie folgt ändern: roh Typ

 ResultTransformer rt = c.getTransformer(); 

public <T> void putIntoACollection(Column<T> c, T columnsValue){ 
     ResultTransformer<T> rt = c.getTransformer(); 
     columnValuePairs.put(c, rt.transform(columnsValue)); 
} 
+0

Willkommen bei stackoverflow - das ist ein toller erster Beitrag! Spot auf. – Bohemian

+0

Dies löst das Problem nicht, da Sie nur Spalten eines Typs einfügen können. Das Problem besteht darin, dass die Spalten verschiedene Arten speichern. – Max

+0

Ich nicht jetzt den gesamten Kontext dieser Reporting-Funktion, aber ich verstehe, dass Spalte ist ein Container, der einen gewissen Wert enthält. Sie können für jeden unterstützten Typ eine beliebige Anzahl von Spalten erstellen. Der Wert in der Spalte kann durch den referenzierten ResultTransformer umgewandelt werden. Dieser ResultTransformer sollte als typsichere Komponente mit demselben Datentyp arbeiten wie die Spalte. Wenn Sie eine beliebige Typhierarchie haben, kann der einzelne ResultTransformer für mehrere Typen auf derselben Hierarchieebene erweitert werden. – ppro

0

Wir konnten alle Vorwände aufgeben, nur der Gott verdammt verwenden, um eine anspruchsvollere Lösung -

static <T> T transform (ResultTransformer<T> rt, Object obj) 
    { 
     T t = (T)obj; // unchecked cast 
     return rt.transform(t); 
    } 


public void putIntoACollection(Column c, Object obj){ 
    ResultTransformer<?> rt = c.getTransformer(); 
    columnValuePairs.put(c, transform(rt, obj)); 

} 
Verwandte Themen