Generics-Problem: Class, Class<?>, Class<Object>

sirbender

Top Contributor
Moderne IDE's (und deren Compiler) aber auch der Java-Compiler tadeln es wenn man parameterisierte Typen parameterlos nutzt. Pflichtbewusst setzt man dann <?> oder <Object> damit diese Warnung weggeht.

Leider fuehrt das zu allerlei Problemen und Inkompatibilitaeren - wobei mich wundert, dass es diese ueberhaupt gibt. Eigentlich sollte Class, Class<?> und Class<Object> doch relativ gleichbedeutend sein, oder?

Ich habe mal ein Codebeispiel angehaengt, das zeigt, was ich meine. Was ist der beste Weg? Vor allem kommen Funktionen wie a(), b() und c() oft aus externen Libraries, wodurch man dann eingeschraenkt ist was man uebergibt.

Java:
import java.util.LinkedHashSet;

public class GenericsProblem {
    
    public static void main(String[] args) {
        LinkedHashSet<Class> s1 = new LinkedHashSet<>();
        a(s1);
        b(s1); // compile error
        c(s1); // compile error
        
        LinkedHashSet<Class<Object>> s2 = new LinkedHashSet<>();
        a(s2); // compile error
        b(s2);
        c(s2); // compile error
        
        LinkedHashSet<Class<?>> s3 = new LinkedHashSet<>();
        a(s3); // compile error
        b(s3); // compile error
        c(s3);
    }

    static void a(Iterable<Class> classes) {}
    static void b(Iterable<Class<Object>> classes) {}
    static void c(Iterable<Class<?>> classes) {}
}
 
Java:
package java_forum.generics;

import java.util.LinkedHashSet;

public class GenericsProblem {

     public static void main(String[] args) {
            LinkedHashSet<Class<Object>> s1 = new LinkedHashSet<>();
            a(s1);
            b(s1);
            c(s1); // compile error: The method b(Iterable<Class<Object>>) in the type GenericsProblem is not applicable for the arguments (LinkedHashSet<Class<?>>)

            
            LinkedHashSet<Class<Object>> s2 = new LinkedHashSet<>();
            a(s2);
            b(s2);
            c(s2); // compile error: The method c(Iterable<Class<?>>) in the type GenericsProblem is not applicable for the arguments (LinkedHashSet<Class<Object>>)
            
            LinkedHashSet<Class<?>> s3 = new LinkedHashSet<>();
            a(s3);
            b(s3); // compile error: The method c(Iterable<Class<?>>) in the type GenericsProblem is not applicable for the arguments (LinkedHashSet<Class<Object>>)
            c(s3);
        }

        static void a(Iterable<? extends Class> classes) {} // geändert gegenüber original post
        static void b(Iterable<Class<Object>> classes) {}
        static void c(Iterable<Class<?>> classes) {}
        
}

Ich habe die Methode a mal so angepasst, dass nur Elemente aus dem Iterable gelesen werden.
Dies ist auch das normale Verhalten.
Es bleiben aber noch Compiler-Fehler übrig.

Den Typ des LinkedHashSet habe ich auch angepasst, um keine Raw-Typ zu haben.
 
Es gibt keinen "besten Weg". Welches Typargument du bei Methodeparameterdeklarationen und Variablendeklarationen verwenden musst, hängt davon ab, wie die Instanz des parametrisierten Typs in der Methode genutzt wird.
Es gibt eine Eselsbrücke, die sich "P.E.C.S." nennt: "Producer Extends, Consumer Super".
Diese sagt soviel aus wie: Wenn der parametrisierte (Container)-Typ lesend verwendet wird, also du auf diesem Methoden aufrufst, deren Rückgabetyp die Typvariable beinhaltet (heißt: Der parametrisierte Typ ist ein "Producer" - er produziert Daten) dann verwende `<? extends Something>`.
Wenn der parametrisierte Typ als Konsument verwendet wird, also du Methoden auf ihm aufrufst, deren Parametertypen Typvariablen sind, dann verwende `<? super Something>`.
Es macht bei näherem Nachdenken tatsächlich alles Sinn und ich empfehle dir dringend, dir die Generics Tutorials von Oracle und von Angelika Langer anzuschauen!

Und: Class, Class<?> und Class<Object> beschreiben allesamt unterschiedliche Typen. Die Tutorials erklären, welche und warum.
 
Java:
package java_forum.generics;

import java.util.LinkedHashSet;

public class GenericsProblem {

     public static void main(String[] args) {
       
            // Typ-Argument Class ist Raw, das untergeordnete Typ-Argument fehlt
            LinkedHashSet<Class> s1 = new LinkedHashSet<>(); // warning: Class is a raw type. References to generic type Class<T> should be parameterized
            a(s1);
            b(s1); // compile error: Typ mit Raw-Typ-Argument passt nicht in Typ mit Typ-Argument mit untergeordneten Typ-Argument
            c(s1); // compile error: Typ mit Raw-Typ-Argument passt nicht in Typ mit Typ-Argument mit untergeordneten Typ-Argument
            d(s1); // compile error: Typ mit Raw-Typ-Argument passt nicht in Typ mit Typ-Argument mit untergeordneten Typ-Argument
           
            // Typ-Argument Class hat ein invariantes untergeordnetes Typ-Argument Object
            LinkedHashSet<Class<Object>> s2 = new LinkedHashSet<>();
            a(s2); // compile error: Typ mit Typ-Argument mit untergeordneten Typ-Argument passt nicht in Typ mit Raw-Typ-Argument
            b(s2);
            c(s2); // compile error: Typ mit Class-Typ-Argument passt nicht in Typ mit ?-Joker-Typ-Argument
            d(s2); // so geht es

            // Typ-Argument Class hat ein untergeordnetes Platzhalter-Typargument, welches keinen Zugriff auf die Elemente erlaubt (also nur Zugriff auf Behälter-Methoden wie size)
            LinkedHashSet<Class<?>> s3 = new LinkedHashSet<>();
            a(s3); // compile error: Typ mit Typ-Argument mit untergeordneten Typ-Argument passt nicht in Typ mit Raw-Typ-Argument
            b(s3); // compile error: Joker ? bedeutet, dass kein Zugriff auf Elemenz erlaubt
            c(s3);
            d(s3);
        }

        static void a(Iterable<Class> classes) {} // warning: untergeordneter Raw-Typ
        static void b(Iterable<Class<Object>> classes) {}
        static void c(Iterable<Class<?>> classes) {}
        static void d(Iterable<? extends Class<?>> classes) {}
}

Ich habe noch mal geändert.
Ich hoffe, die Kommentare erklären die einzelnen Fälle.
Unten gibts noch die Methode d, die manche Aufrufe komoilierbar macht.
 

Zurück
Oben