Generics: Wildcard und Object

darekkay

Bekanntes Mitglied
Ich habe ein recht spezielles Problem mit Generics. Dazu erstmal ein Beispielcode:

Code:
public class Main
{

    static class MyThirdPartyLibrary
    {
        public MyThirdPartyLibrary()
        {

        }

        public static List<Column<?>> getColumns()
        {
            List<Column<?>> columns = new ArrayList<Column<?>>();

            Column<?> column1 = new Column<String>();
            Column<?> column2 = new Column<Integer>();

            columns.add(column1);
            columns.add(column2);

            return columns;
        }
    }

    public static void main(String[] args)
    {
        new Main();
    }

    public Main()
    {

        for (Column<?> column : MyThirdPartyLibrary.getColumns())
        {
            column.setCell(new AbstractCell()
            {

                @Override
                public void doSomething(Object muh)
                {
//
                }

            });
        }
    }

    static class Column<U>
    {
        public void setCell(Cell<U> cell)
        {

        }
    }

    interface Cell<U>
    {
        void doSomething(U muh);
    }

    abstract class AbstractCell<U> implements Cell<U>
    {

    }

}

Eine Methode einer Library ((Ext-)GWT falls es wen interessiert) liefert mir eine List<Column<?>>. Ich möchte diese durchgehen, und jeder Spalte eine AbstractCell zuweisen. Der Code funktioniert, allerdings bekomme ich (offensichtlich) eine Warning: "AbstractCell is a raw type".
Nun, das waren meine bisherigen Versuche, diese wegzubekommen:

1.) new AbstractCell<Object>()
Das führt zu einem Error:
Code:
The method setCell(Main.Cell<capture#3-of ?>) in the type Main.Column<capture#3-of ?> is not applicable for the arguments (new Main.AbstractCell<Object>(){})

2.) (List<Column<Object>>) MyThirdPartyLibrary.getColumns()
Auch der Cast schlägt fehl:
Cannot cast from List<Main.Column<?>> to List<Main.Column<Object>>
Ich nehme einfach mal hin, dass man ? und Object nicht gleichsetzen oder casten kann. Wie kann ich nun die "raw type" warning "sauber" wegbekommen, oder diese zu unterdrücken? Das Problem ist, dass ich auf die Methode getColumns keinen Einfluss habe, und diese Wildcards benutzt. Und wieso klappt mein Versuch nicht, Object einzusetzen, um dann mittels instanceof den Typ zu überprüfen?

3.) new AbstractCell<?>
Klar, war rauskommt:
The type new AbstractCell(){} cannot extend or implement Main.AbstractCell<?>. A supertype may not specify any wildcard


Vielen Dank schon mal!
 
Es geht nicht. Und das ist auch gut und richtig so. Alles andere würde die Typsicherheit gefährden:

Java:
public class Main
{
    static class MyThirdPartyLibrary
    {
        // I know that I only have 'Integer' columns
        private static List<Column<Integer>> columns = new ArrayList<Column<Integer>>();

        // But I won't tell ya :D 
        public static List<Column<?>> getColumns()
        {
            Column<Integer> column1 = new Column<Integer>();
            Column<Integer> column2 = new Column<Integer>();
            columns.add(column1);
            columns.add(column2);
            return columns;
        }

        public static void processThem()
        {
            Column<Integer> column = columns.get(0); // This works
            Integer integer = column.getValue(); // This also works...
            // except when someone put in something that
            // is not an Integer - then this will crash...
        }
    }

Wenn man dann sowas machen würde
Java:
    public Main()
    {
        Cell<String> stringCell = ...
        MyThirdPartyLibrary.getColumns().get(0).setCell(stringCell);
        MyThirdPartyLibrary.processThem();
    }
und die zweite Zeile so funktionieren WÜRDE, dann würde es ihn bei "processThem" raushauen, weil dort kein Integer drin steht.

Man kann sinngemäß(!) sagen, dass eine List<?> sozusagen "read only" ist - man weiß einfach nicht, was man reinpacken darf und was nicht (außer [c]null[/c] 😉 )

Wie man damit umgeht... kann man pauschal kaum sagen, hängt vom Anwendungsfall ab...
 
Also bedeutet "?" nichts anderes, als eine Typvariable - bloß dass man diese nicht wiederverwenden möchte? Dann macht es Sinn. Und ich kann nichts dagegen machen, da die Library zwar Generics nutzt, diese für mein Beispiel (verschieden parametrisierte Columns) aber keine Rolle spielen, da ich trotzdem reinpacke, wonach mir gerade ist (und das auch so gewollt ist, da man bsp. eine Integer- und eine String-Spalte haben möchte). Dann muss ich die Warning doch unterdrücken.

Vielen Dank!
 
mit der Liste hast du hier direkt auch kein Problem, das add() geht ja,
nur ist nicht bekannt, welchen Typ die unterschiedlichen Columns haben,

verkürzt kann man sich ohne Liste auf
> Column<?> column1 = new Column<String>();
stürzen, wobei in dieser einzelnen Variable unklar ist, warum der Typ nicht in der Variablen steckt,
aber wenn eben, dann fehlt diese Information,

Object kann es nicht sein bzw. wäre vollkommen unsicher,
analog zum List-Beispiel kennt jemand die Column vielleicht als <String>, was ja der wahre Typ ist, will später String auslesen,
da wäre es fatal wenn du nur ein Object hineinspeicherst, auch wenn es hier im Moment keine entsprechende Methode gibt,

wenn der Typ unbekannt ist entfällt Generics sowieso,
was ansonsten passieren soll, welche Cell für alle oder einige Columns geeignet ist, ist hier nicht erkennbar
 
Wow, ich konnte es doch noch lösen. Man merkt, dass ich mich mit den advanced techniques für generics beschäftigen sollte ^^
Ich habe die problematische Stelle einfach in eine generische Methode ausgelagert und eine Typvariable vergeben:
Code:
    public Main()
    {
        for (Column<?> column : MyThirdPartyLibrary.getColumns())
        {
            genericMethod(column);
        }
    }

    protected <U> void genericMethod(final Column<U> column)
    {
        column.setCell(new AbstractCell<U>()
        {

            @Override
            public void doSomething(U muh)
            {
                // 
            }
        });
    }
Wie gesagt, die Typsicherheit innerhalbt der Liste ist so oder so nicht gegeben, da die List<Column<?>> gleichzeitig verschiedene Spaltentypen enthält. Nun ist aber immerhin die Warnung weg.
 
Für so einen konkreten Anwendungsfall ist das Erstellen einer solchen Methode zumindest nicht unüblich (d.h. für einen Fall, wo man das ""U"" nicht schon hat oder kennt oder gar selbst festlegen will)
 

Zurück
Oben