2012-04-05 8 views
2

Ich bin auf der Suche nach einem schönen Muster, um Folgendes zu tun: Ich habe eine Klasse FooBar<A,B> und ich weiß, dass A hat einen Konstruktor mit einem einzigen B Parameter. Ich möchte eine geschützte Methode in FooBar mit der folgenden Signatur haben:Java Generics: Instantiierung A mit einem einzigen Konstruktor Argument B

protected A chuckNorris42(B b); // intent: return new A(b) 

Natürlich ist die oben erwähnte „Absicht“ nicht übersetzbar ist. Zur Umgehung des Problems habe ich diese abstrakte Methode und an dem Punkt, wo ich FooBar mit konkreten Klassen instanziiert implementiert I chuckNorris42 inline:

FooBar<Chuck, Norris> = new FooBar<Chuck, Norris>() { 
    protected Chuck chuckNorris42(Norris norris) { 
    return new Chuck(norris); 
    } 
} 

Es funktioniert, aber ich fragte mich, ob es einen schöneren oder sauberer Weg ist, das Gleiche zu erreichen Also habe ich beschlossen, die Frage hier zu stellen.

Antwort

4

Der Punkt ist, Sie wissen nicht, dass jede tatsächliche Instanz von A solch einen Konstruktor hat, das Java-Typ-System ist einfach nicht stark genug erzwinge dies. Daher gibt es keinen sauberen generischen eingebauten Weg, um den Konstruktor einer Klasse aufzurufen, die Sie nicht statisch kennen. Sie müssen die Reflexion wie in den anderen Antworten verwenden, aber dies kann natürlich zur Laufzeit fehlschlagen, wenn A plötzlich mit einem Typ instanziiert wird, der entweder nicht instanziierbar ist (A kann zum Beispiel eine abstrakte Klasse sein!), Oder nicht geeigneter Konstruktor. Wenn Sie dies vermeiden wollen, könnten Sie Fabriken verwenden:

interface Factory<A, B> { 
    A createInstance(B b); 
} 

dann den Benutzer Ihrer Klasse lassen FooBar geben Sie eine Instanz dieser Fabrik für die aktuellen Werte der Typen A und B.

0

können Sie versuchen, Reflexion, so etwas wie

public A produce(Class<A> clazz, B arg) throws IllegalArgumentException, SecurityException, InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchMethodException { 
    return clazz.getConstructor(arg.getClass()).newInstance(arg); 
} 
+0

Hum, doesn 't type wrate verbieten, so etwas direkt in die generische Klasse zu schreiben? –

+0

AFAIK gibt es keine Möglichkeit, auf die Klasse eines generischen Arguments zuzugreifen –

+0

Ja, tut mir leid. Sie sollten eine Klasse von Chuck in eine Funktion übergeben. –

2

Ich bin nicht sicher, wie viel es ist schöner und sauberer, aber man kann so etwas versuchen:

public class FooBar<A,B> { 
private Class<A> aClass; 
public FooBar(Class<A> aClass) { 
    this.aClass = aClass; 
} 

protected A chuckNorris42(B b) throws NoSuchMethodException, InstantiationException,IllegalAccessException,InvocationTargetException{ 
    return aClass.getConstructor(b.getClass()).newInstance(b); 
} 
} 

Im moment gibt es keine Möglichkeit, Klasse eines generischen Arguments erhalten. Eine der Methoden, um das Objekt der generischen Argumentklasse zu instanziieren, besteht darin, die Klasse im Konstruktor zu übergeben.

UPDATE: auch ein Weg, um generische Argumente in der Hierarchie aufgeteilt ist:

public abstract class Foobar<A,B> { 
    protected abstract A chuckNorris(B b); 
} 

public class FooBarChuck<B> extends Foobar<Chuck<B>,B>{ 

    @Override 
    protected Chuck<B> chuckNorris(B b) { 
     return new Chuck<B>(b); 
    } 
} 
public class Chuck<B> { 
    public Chuck(B b) { 
    //something here 
    } 
} 

Dann müssen Sie diese Methode nicht für alle B-Klassen neu implementieren, aber sie haben immer noch zu Erstelle separate Klassen für jeden Chuck.

P.S. Ich glaube nicht, dass diese Methoden viel sauberer als deine sind :)

Verwandte Themen