web-dev-qa-db-fra.com

Java Thencoming Signature générique Signature

Pourquoi la Déclaration ressemble-t-elle à ceci:

default <U extends Comparable<? super U>> Comparator<T> thenComparing(
            Function<? super T, ? extends U> keyExtractor)

Je comprends la plupart d'entre eux. Il est logique que U peut être quelque chose aussi longtemps qu'il est comparable à une superclasse d'elle-même et donc aussi comparable à elle-même.

Mais je n'ai pas cette partie: Function<? super T, ? extends U>

Pourquoi pas seulement avoir: Function<? super T, U>

Les U ne peuvent pas simplement paramétrer ce que le KeyExtractor retourne et prolonger toujours Comparable<? super U> tous les mêmes?

12
kng

Il semble que votre question concerne de type arguments en général pour ma réponse, je séparerai les arguments de type que vous avez fournis des types auxquels ils appartiennent, dans ma réponse, pour la simplicité.

Nous devrions d'abord noter qu'un type paramétré de caractères génériques est incapable d'accéder à ses membres qui sont du paramètre de type respectif. C'est pourquoi, dans votre cas spécifique, le ? extends U Peut être substitué à U et travaille toujours bien.

Cela ne fonctionnera pas dans tous les cas. L'argument de type U n'a pas la polyvalence et la sécurité de type supplémentaire que ? extends U A. Les caractères génériques sont un argument de type unique dans lequel les instanciations des types paramétrés (avec des arguments de type générique) ne sont pas aussi restreintes par l'argument de type car elles seraient si l'argument de type était un paramètre de type ou de type concret; Les caractères génériques sont essentiellement placent les supports plus généraux que les paramètres de type et les types de béton (lorsqu'ils sont utilisés comme arguments de type). la première phrase du Java tutoriel sur des cartes sauvages Lit:

En code générique, le point d'interrogation (?), Appelé Wildcard, représente un type inconnu.

Pour illustrer ce point, jetez un coup d'œil à cela

class A <T> {}

maintenant, faisons deux déclarations de cette classe, une avec un type de béton et l'autre avec une carte sauvage, puis nous les instantions

A <Number> aConcrete = new A <Integer>(); // Compile time error
A <? extends Number> aWild = new A<Integer>() // Works fine

Cela devrait donc illustrer la manière dont un argument de type générique ne limite pas l'instanciation autant qu'un type concret. Mais qu'en est-il d'un paramètre de type? Le problème avec l'utilisation de paramètres de type est mieux manifesté dans une méthode. Pour illustrer examiner cette classe:

class C <U> {
    void parameterMethod(A<U> a) {}
    void wildMethod(A<? extends U> a) {}
    void test() {
        C <Number> c = new C();
        A<Integer> a = new A();
        c.parameterMethod(a); // Compile time error
        c.wildMethod(a); // Works fine
    }

Notez comment les références c et a sont des types concrets. Maintenant, cela a été abordé dans une autre réponse, mais ce qui n'a pas été traité dans l'autre réponse, c'est la manière dont le concept d'arguments de type concernait l'erreur de temps de compilation (pourquoi un argument de type provoque une erreur d'heure de compilation et l'autre ne pas) et ceci La relation est la raison pour laquelle la Déclaration en question est déclarée avec la syntaxe qu'elle est déclarée. Et cette relation est le type de sécurité et de la polyvalence de type supplémentaire fournit des paramètres de type et non de la convention de frappe. Maintenant, pour illustrer ce point, nous devrons donner A un membre du paramètre type, donc:

class A<T> { T something; }

Le danger d'utiliser un paramètre de type dans le paramètrethod () est que le paramètre Type peut être mentionné sous la forme d'une distribution, ce qui permet d'accéder au membre something.

class C<U> {
    parameterMethod(A<U> a) { a.something = (U) "Hi"; }
}

Ce qui permet à son tour la possibilité de pollution de tas. Avec cette implémentation du paramètreéthod, la déclaration C<Number> c = new C(); dans la méthode Test () pourrait provoquer la pollution des tas. Pour cette raison, le compilateur émet une erreur d'heure de compilation lorsque des procédés avec des arguments de paramètre de type sont transmis n'importe quel objet sans se déclencher de la classe des paramètres de type déclarant la classe; De même, un membre du paramètre de type émettra une erreur de temps de compilation s'il est instancié à n'importe quel objet sans distribution de la classe de déclaration du type Paramètre. La chose vraiment importante ici au stress est sans couler car vous pouvez toujours passer des objets à une méthode avec un argument de paramètre de type, mais il doit être coulé à Ce paramètre de type (ou dans ce cas, jeté sur le type contenant le paramètre de type). Dans mon exemple

    void test() {
        C <Number> c = new C();
        A<Integer> a = new A();
        c.parameterMethod(a); // Compile time error
        c.wildMethod(a); // Works fine
    }

c.parameterMethod(a) fonctionnerait si a a été lancé sur A<U>, donc si la ligne ressemblait à ceci c.parameterMethod((A<U>) a); Aucune erreur de compilation ne se produirait, mais vous obtiendrez Erreur CastClasseSexitSExection si vous avez essayé de définir une variable int variable égale à a.something après que le parameterMethod() est appelé (et encore, le compilateur nécessite la distribution car U pourrait représenter quoi que ce soit). Tout ce scénario ressemblerait à ceci:

    void test() {
        C <Number> c = new C();
        A<Integer> a = new A();
        c.parameterMethod((A<U>) a); // No compile time error cuz of cast
        int x = a.something; // doesn't issue compile time error and will cause run-time ClassCastException error
    }

Ainsi, étant donné qu'un paramètre de type peut être référencé sous la forme d'une distribution, il est illégal de transmettre un objet de l'intérieur des paramètres de type déclarant la classe à une méthode avec un argument d'un paramètre de type ou contenant un paramètre de type. Un caractère générique ne peut pas être référencé sous la forme d'une coulée, de sorte que le a in wildMethod(A<? extends U> a) n'a pas pu accéder au membre T d'un; En raison de cette sécurité supplémentaire de type, car cette possibilité de pollution des tas est évitée avec une carte générique, le Java compilateur permet un type de béton transmis au Wildmethod sans couler lorsqu'il est invoqué par la référence C dans C<Number> c = new C(); également, c'est pourquoi un type paramétré de caractères génériques peut être instancié à un type de béton sans couler. Quand je dis une polyvalence des arguments de type, je parle de quelles instanciations ils permettent dans leur rôle de Un type paramétré; et quand je dis une sécurité supplémentaire de type, je parle de l'incapacité de référencer des caractères génériques sous la forme d'une coulée qui contourne le tasPollution.

Je ne sais pas pourquoi quelqu'un lancerait un paramètre de type. Mais je sais que un développeur profiterait au moins de la polyvalence des caractères génériques VS un paramètre de type. J'ai peut-être écrit cela avec confusion, ou peut-être mal compris votre question, votre question me semble-t-elle que de trouver des arguments de type en général au lieu de cette déclaration spécifique. Aussi si keextractor de la déclaration Function<? super T, ? extends U> keyExtractor Est utilisé de manière à ce que les membres appartenant à Function du deuxième paramètre de type ne soient jamais accessibles, puis à nouveau, des caractères génériques sont parfaits parce qu'ils ne peuvent pas éventuellement accéder à ces membres quand même; Alors, pourquoi un développeur ne voudrait-il pas vouloir la polyvalence mentionnée ici que les caractères génériques fournissent-ils? Ce n'est qu'un avantage.

1
Matthew S.