Mehrere Wildcards auf einer generischen Methode machen den Java-Compiler (und mich!) Sehr verwirrt

Betrachten wir zunächst ein einfaches Szenario sehe vollständige Quelle auf ideone.com):

import java.util.*;

public class TwoListsOfUnknowns {
    static void doNothing(List<?> list1, List<?> list2) { }

    public static void main(String[] args) {
        List<String> list1 = null;
        List<Integer> list2 = null;
        doNothing(list1, list2); // compiles fine!
    }
}

Die beiden Platzhalter haben nichts miteinander zu tun, weshalb Sie @ anrufen könnedoNothing mit einerList<String> und einList<Integer>. Mit anderen Worten, die beiden? kann sich auf völlig unterschiedliche Typen beziehen. Daher kompiliert folgendes nicht, was zu erwarten ist auch auf ideone.com):

import java.util.*;

public class TwoListsOfUnknowns2 {
    static void doSomethingIllegal(List<?> list1, List<?> list2) {
        list1.addAll(list2); // DOES NOT COMPILE!!!
            // The method addAll(Collection<? extends capture#1-of ?>)
            // in the type List<capture#1-of ?> is not applicable for
            // the arguments (List<capture#2-of ?>)
    }
}

So weit so gut, aber hier fangen die Dinge an, sehr verwirrend zu werden as gesehen auf ideone.com):

import java.util.*;

public class LOLUnknowns1 {
    static void probablyIllegal(List<List<?>> lol, List<?> list) {
        lol.add(list); // this compiles!! how come???
    }
}

Der obige Code kompiliert für mich in Eclipse und aufsun-jdk-1.6.0.17 in ideone.com, aber sollte es? Ist es nicht möglich, dass wir ein @ habList<List<Integer>> lol und einList<String> list, die analogen zwei nicht verwandten Platzhalter-Situationen vonTwoListsOfUnknowns?

In der Tat wird die folgende geringfügige Änderung in diese Richtung nicht kompiliert, was zu erwarten ist as gesehen auf ideone.com):

import java.util.*;

public class LOLUnknowns2 {
    static void rightfullyIllegal(
            List<List<? extends Number>> lol, List<?> list) {

        lol.add(list); // DOES NOT COMPILE! As expected!!!
            // The method add(List<? extends Number>) in the type
            // List<List<? extends Number>> is not applicable for
            // the arguments (List<capture#1-of ?>)
    }
}

So sieht es so aus, als ob der Compiler seine Arbeit macht, aber dann bekommen wir das as gesehen auf ideone.com):

import java.util.*;

public class LOLUnknowns3 {
    static void probablyIllegalAgain(
            List<List<? extends Number>> lol, List<? extends Number> list) {

        lol.add(list); // compiles fine!!! how come???
    }
}

Again, wir können z.B. einList<List<Integer>> lol und einList<Float> list, also sollte das nicht kompilieren, oder?

In der Tat gehen wir zurück zum einfacherenLOLUnknowns1 (zwei unbegrenzte Platzhalter) und versuchen zu sehen, ob wir tatsächlich @ aufrufen könnprobablyIllegal in irgendeiner Weise. Versuchen wir zuerst den "einfachen" Fall und wählen den gleichen Typ für die beiden Platzhalter as gesehen auf ideone.com):

import java.util.*;

public class LOLUnknowns1a {
    static void probablyIllegal(List<List<?>> lol, List<?> list) {
        lol.add(list); // this compiles!! how come???
    }

    public static void main(String[] args) {
        List<List<String>> lol = null;
        List<String> list = null;
        probablyIllegal(lol, list); // DOES NOT COMPILE!!
            // The method probablyIllegal(List<List<?>>, List<?>)
            // in the type LOLUnknowns1a is not applicable for the
            // arguments (List<List<String>>, List<String>)
    }
}

Das macht keinen Sinn! Hier versuchen wir nicht einmal, zwei verschiedene Typen zu verwenden, und es wird nicht kompiliert! Machen Sie es zu einemList<List<Integer>> lol undList<String> list gibt auch einen ähnlichen Übersetzungsfehler! Nach meinen Experimenten besteht die einzige Möglichkeit, den Code zu kompilieren, darin, dass das erste Argument ein explizites @ isnull Art as gesehen auf ideone.com):

import java.util.*;

public class LOLUnknowns1b {
    static void probablyIllegal(List<List<?>> lol, List<?> list) {
        lol.add(list); // this compiles!! how come???
    }

    public static void main(String[] args) {
        List<String> list = null;
        probablyIllegal(null, list); // compiles fine!
            // throws NullPointerException at run-time
    }
}

So sind die Fragen, in Bezug aufLOLUnknowns1, LOLUnknowns1a undLOLUnknowns1b:

Welche Arten von Argumenten hatprobablyIllegal akzeptierenSolltelol.add(list); überhaupt kompilieren? Ist es typsicher?Ist dies ein Compiler-Fehler oder habe ich ein Missverständnis mit den Capture-Konvertierungsregeln für Platzhalter?Anhang A: Double LOL?

Wenn jemand neugierig ist, kompiliert dies gut as gesehen auf ideone.com):

import java.util.*;

public class DoubleLOL {
    static void omg2xLOL(List<List<?>> lol1, List<List<?>> lol2) {
        // compiles just fine!!!
        lol1.addAll(lol2);
        lol2.addAll(lol1);
    }
}
Anhang B: Verschachtelte Platzhalter - was bedeuten sie wirklich ???

Weitere Untersuchungen haben ergeben, dass möglicherweise mehrere Platzhalter nichts mit dem Problem zu tun haben, sondern vielmehr ein verschachtelt Platzhalter ist die Quelle der Verwirrung.

import java.util.*;

public class IntoTheWild {

    public static void main(String[] args) {
        List<?> list = new ArrayList<String>(); // compiles fine!

        List<List<?>> lol = new ArrayList<List<String>>(); // DOES NOT COMPILE!!!
            // Type mismatch: cannot convert from
            // ArrayList<List<String>> to List<List<?>>
    }
}

So sieht es vielleicht aus wie einList<List<String>> ist keinList<List<?>>. In der Tat, während jederList<E> ist einList<?>, es sieht nicht aus wie einList<List<E>> ist einList<List<?>> (as gesehen auf ideone.com):

import java.util.*;

public class IntoTheWild2 {
    static <E> List<?> makeItWild(List<E> list) {
        return list; // compiles fine!
    }
    static <E> List<List<?>> makeItWildLOL(List<List<E>> lol) {
        return lol;  // DOES NOT COMPILE!!!
            // Type mismatch: cannot convert from
            // List<List<E>> to List<List<?>>
    }
}

Eine neue Frage stellt sich dann: Was ist einList<List<?>>?

Antworten auf die Frage(6)

Ihre Antwort auf die Frage