2014-10-02 9 views
5

Wenn ich zum Beispiel eine Methode, die varargs für Klassentypen verwendet, die wie dieses eine Superklasse erweitert:varargs vom Typ Klasse in Java

public static <E extends Example> void test(Class<E>... es){} 

Dann versuche ich mit zwei diese Methode zu nennen verschiedenen Unterklassen von Beispiel kann ich nur tun, wenn ich ein Array mit den zwei Klassen darin mache.

//this does not work 
test(E1.class,E2.class); 
//this does work 
test(new Class[]{E1.class,E2.class}); 
public class E1 extends Example {} 
public class E2 extends Example {} 

Warum ist das?

Antwort

6

Diese Zeile nicht kompiliert:

test(E1.class,E2.class); 

Es gibt nur einen Typ-Parameter ist E und Java müssen die abgeleiteten Typen der Argumente genau übereinstimmen. Es kann Example nicht abgeleitet werden, da die Objekte Class<E1> und Class<E2>, nicht Class<Example> sind. Die Invarianz von Java-Generika verhindert dies.

Sie können durch die Einführung einer Obergrenze Wildcard auf test ‚s generischen Typparameter dieses Problem umgehen:

public static <E extends Example> void test(Class<? extends E>... es) 

Dies ermöglicht Java Example für E, zu schließen, indem die obere Grenze Wildcard mit E1 und E2 erfüllen.

In der zweiten Zeile wird ein unformatiertes Array von Class Es erstellt, wobei Generika umgangen werden und eine Warnung "Ungeprüfter Aufruf" generiert wird.

new Class[]{E1.class,E2.class} 

Wenn Sie eine Art Argument Class hier zur Verfügung zu stellen, um zu versuchen, würden Sie einen Compiler-Fehler mit jedem halbwegs vernünftigen Art Parameter erhalten:

// Needs Class<Example> but found Class<E1> and Class<E2> 
test(new Class<Example>[]{E1.class,E2.class}); 

// Needs Class<E1> but found Class<E2> 
test(new Class<E1>[]{E1.class,E2.class}); 

// Needs Class<E2> but found Class<E1> 
test(new Class<E2>[]{E1.class,E2.class}); 

Satisfying den Schluss über einen Platzhalter Hier wird nur das eigentliche Problem aufgedeckt - generische Array-Erstellung.

// Generic array creation 
test(new Class<? extends Example>[]{E1.class,E2.class}); 
+0

Vielen Dank. Es war nie schädlich für mein Programm, aber ich habe mich definitiv eine Weile an den Kopf gekratzt. Ich schätze die Aufklärung sehr. – Squirvin

2

Sie definieren das generische E einer einzelnen Klasse, die Beispiel erweitert. Sie können in Ihrem Aufruf nicht zwei verschiedene Klassen referenzieren, da sie nicht wissen, welcher Typ E ist. Es erwartet nur einen Typ.

Obwohl dies nicht funktioniert:

test(E1.class, E2.class); 

Dies tut:

test(E1.class, E1.class); 

Der Grund, warum Sie es mit einem Array tun kann, ist wegen der Typ Löschung. Der Compiler sieht nicht, dass die Klassen innerhalb des Arrays unterschiedlich sind.

Wenn Sie Ihre Methode ändern, um jede Klasse zu akzeptieren, die Example erweitert, wird es funktionieren.

public static void test(Class<? extends Example>...classes) 
+1

Ich denke, Sie haben es ziemlich gut erklärt. Aber um dem Beispiel des OP's zu entsprechen, sollte Ihre Signatur 'public static void test (Class ... classes) sein '. Es wird (in jedem Fall) jedoch eine "-Xlint: unchecked" Warnung geben. – 5gon12eder

+0

@ 5gon12eder: Die Signatur, die Sie angegeben haben, und die in dieser Antwort sind genau gleichwertig, d. H. Sie akzeptieren genau die gleiche Klasse von Argumenten. Der einfachere (der in dieser Antwort angegebene) sollte immer bevorzugt werden, da der type-Parameter unnötig ist. – newacct

+0

@newacct Sie akzeptieren die gleiche Klasse von Argumenten, aber wenn Sie auf den generischen Typ verweisen möchten, benötigen Sie die ausführlichere Syntax. Da in der OP 'test'' void' zurückgibt und einen leeren Körper hat, könnten Sie argumentieren, dass wir uns nicht auf den Typ beziehen wollen, aber im Allgemeinen ist die ausführliche Syntax auch mächtiger. – 5gon12eder

Verwandte Themen