2016-09-10 6 views
2

Angenommen, ich habe und API-Methode erklärt, wie folgendKlasse <Result> kann nicht in Klasse umgewandelt werden

public class Dummy { 
    public static class Result<ValueT extends Comparable> { 
     public ValueT value; 
    } 

    public static <ValueT extends Comparable> Result<ValueT> 
      getResult(Class<? extends Result<ValueT>> ofType) throws Exception { 
     return ofType.newInstance(); 
    } 
} 

Jetzt würde Ich mag es berufen sich auf Java Compiler-Typprüfung und kann einfach‘ t korrekte Syntax finden, dies zu tun:

Versuche:

getResult(Result<Integer>.class)<-- expected syntax

public static void invokeGetResult() { 
    Result<Integer> intResult = getResult(Result<Integer>.class); 
} 

Ergebnisse in

error: <identifier> expected 
    Result<Integer> intResult = getResult(Result<Integer>.class); 
                 ^

getResult(Result.class)<-- just to try

public static void invokeGetResult() { 
    Result<Integer> intResult = getResult(Result.class); 
} 

Ergebnisse in

error: method getResult in class Dummy cannot be applied to given types; 
    Result<Integer> intResult = getResult(Result.class); 
           ^
    required: Class<? extends Result<ValueT>> 
    found: Class<Result> 
    reason: cannot infer type-variable(s) ValueT 
    (argument mismatch; Class<Result> cannot be converted to Class<? extends Result<ValueT>>) 
    where ValueT is a type-variable: 
    ValueT extends Comparable declared in method <ValueT>getResult(Class<? extends Result<ValueT>>) 

getResult((Class<Result<Integer>>)Result.class)<-- just to try

public static void invokeGetResult() { 
    Result<Integer> intResult = getResult((Class<Result<Integer>>)Result.class); 
} 

Ergebnisse in

error: incompatible types: Class<Result> cannot be converted to Class<Result<Integer>> 
    Result<Integer> intResult = getResult((Class<Result<Integer>>)Result.class); 
                     ^
+0

sollten Sie ' newacct

Antwort

-1

EDIT: Ich verstand nicht, deutlich die ursprünglichen Absichten. Dieses Beispiel sollte für Ihren Anruf funktionieren, allerdings ist es ein bisschen hacky und nicht so empfehlenswert.

public class Dummy { 
    public static class Result<ValueT extends Comparable> { 
     public ValueT value; 
    } 

    public static <ValueT extends Comparable> Result<ValueT> 
     getResult(Class<? extends Result<ValueT>> ofType) { 
     return null; 
    } 
} 

Sie können es so nennen:

Dummy.Result<Integer> result = Dummy.getResult(new Result<Integer>(){}.getClass()); 

Die Kompilierung Überprüfung mit diesem Beispiel arbeiten soll, wie Sie eine anonyme leere Klasse erstellen und es gibt Informationen erfolgreich abgerufen werden können.

Sie könnten auch eine Typumwandlung machen wie @Jorn in Kommentaren vorgeschlagen, obwohl Ihr Compiler Sie davor warnen würde, dass es sich um eine "ungeprüfte Besetzung" handelt.

+0

Nein, diese Übervereinfachung des ursprünglichen Anwendungsfalls - verschiedene Unterklassen von 'Dummy.Result' sollten bedingt abhängig vom Argument erstellt werden, deshalb wird die Methode als' getResult (Klasse > ofType) ' –

+0

Dann ist die einzige Möglichkeit, eine Klasse zu implementieren, die' Result 'erweitert und als 'getResult'-Parameter übergeben. Soweit ich weiß, tippe in alle in den Fragen gegebenen Beispiele Löschkicks ein, und ich bin mir nicht sicher, ob es einen einfacheren Weg gibt, dies zu tun, ohne neue Klassen zu erstellen. –

+0

Endgültige 'Klasse' ist nur durch den Client-Code bekannt. Das Problem tritt in der intermediären API-Methode auf, in der Code nur über eine Basis-Ergebnisklasse und einen Werttyp bekannt ist. –

0

Wenn der einzige Zweck der Class<...> ist, es instanziieren, ziehen Sie in Betracht, stattdessen eine Supplier<...> zu verwenden. Es hat dieses Problem nicht und löst keine Ausnahmen aus. Hier

ein Beispiel:

class SomeClass extends Result<Integer> {...} 

public static <T extends Result<?>> T getResult(Supplier<T> constructor) { 
    return constructor.get(); 
} 

Verwendung wie:

getResult(SomeClass::new); // Passes the constructor. 
+0

Leider funktioniert das nicht in meinem Anwendungsfall - das resultierende Objekt wird tatsächlich über die De-Serialisierungs- und Deserialisierungs-API erstellt, die im Gebrauch die Klasse als Typinformation annimmt. Es kann sinnvoll sein, die Serialisierungs-API neu zu definieren, um den Lieferanten zu akzeptieren - aber das bringt andere Probleme mit sich: (1) Typinformationen sind erst verfügbar, nachdem die Instanz erstellt wurde (via instance.getClass()) und (2) nicht möglich ist Übergeben Sie eine abstrakte Klasse - in einigen Fällen Serialisierung API erstellen Sie bestimmte Unterklasse abhängig von der Laufzeitkonfiguration. –

+0

Ich bin mir nicht sicher, ob Sie das tun können, was Sie in Java versuchen. Typinformationen werden nicht in generischen Klassen gespeichert, weil sie gelöscht werden und keine Verdinglichung. –

+0

@XtraCoder Welche De-Serialisierungs-API verwenden Sie? Sie haben normalerweise ihre eigenen Arten, mit generischen Klassen umzugehen. –

0

Es ist nicht wirklich so etwas wie eine Class<Result<Integer>>. Class Objekte repräsentieren replizierbare Klassen zur Laufzeit, und zur Laufzeit existiert nur ein einziges Class Objekt für die Klasse Result, das Sie über den Ausdruck Result.class mit dem Typ Class<Result> erhalten. Es gibt keine Class Objekte, die Result<Integer> oder Result<String> usw. darstellen.

Sie den Ausdruck des Typs nehmen Class<Result> (die Sie von Result.class bekommen) und tun auf sie ein paar unsichere Abgüsse in eine Class<Result<Integer>> oder so etwas zu drehen, zum Beispiel:

`(Class<Result<Integer>>)(Class<?>)Result.class` 

aber nicht das ist unsicher. Es ist unsicher, da die Schnittstelle von Class über bestimmte Methoden verfügt, die T (den Typparameter der Klasse Class) basierend auf Laufzeittypoperationen mit der Klasse zurückgeben. Beispielsweise hat Class eine Methode .cast(), die überprüft, ob das übergebene Objekt eine Instanz der Klasse ist, und wenn nicht, löst eine Ausnahme aus, und wenn dies der Fall ist, wird das Objekt als Typ T zurückgegeben. Wenn Sie Result.class.cast() aufrufen, gibt es eine Result zurück, und die Methode überprüft in der Tat zur Laufzeit, dass das Objekt eine Result ist. Aber wenn Sie .cast() für einen Ausdruck des Typs Class<Result<Integer>> aufrufen, gibt es den Typ Result<Integer> zurück, aber die Klasse konnte zur Laufzeit nicht überprüfen, dass die Sache ein Result<Integer> ist, weil 1) das gleiche Result Klassenobjekt ist und 2) allgemeine Informationen existiert zur Laufzeit nicht. So erhalten Sie eine Result<Integer>, die möglicherweise kein Result<Integer> ist.

Also im Grunde müssen Sie darüber nachdenken, was Sie dieses Klassenobjekt zu tun, und wie es sinnvoll ist, es als Class<Result<Integer>> zu haben, wenn alles zur Laufzeit ein Klassenobjekt ist, das den Rohtyp Result darstellt.

Hier haben Sie eine Methode, die Class<? extends Result<ValueT>> dauert. Ich frage mich, warum du hier die Wildcard hast. Versuchen Sie, Subklassen von Result akzeptieren zu lassen? Denn wenn nicht, dann ist das einzige Objekt, das übergeben werden kann, das eindeutige Result Klassenobjekt selbst, was bedeutet, dass der Parameter im Grunde sinnlos ist, weil es immer die gleiche Sache ist, die übergeben werden kann; in diesem Fall, dass Sie könnte genauso gut die Parameter loszuwerden und einfach tun:

public static <ValueT extends Comparable<? super ValueT>> Result<ValueT> 
     getResult() throws Exception { 
    return new Result<ValueT>(); 
} 

Wenn Sie vorhaben, Subklassen von Result zu akzeptieren, dann vorausgesetzt, Sie, dass alle Subklassen einen No-Parameter-Konstruktor haben muss, wie Sie rufen .newInstance() darauf, was schlechtes Design ist. Sie sollten etwas wie ein Supplier verwenden, wie Jorn Vernees Antwort suggeriert.

Verwandte Themen