Java: Résolution de compilation-temps et «méthode la plus spécifique» telle qu'elle s'applique à l'arité variable

StackOverflow https://stackoverflow.com/questions/6023439

  •  14-11-2019
  •  | 
  •  

Question

Quelqu'un pourrait-il m'aider à comprendre Section 15.12.2.5 du JLS RE: Méthode la plus spécifique?

(Cut et pâte matraquées de JLS suit)

De plus, une méthode de membre ARITY variable nommé M est plus spécifique qu'une autre méthode de membre ARITY variable du même nom si l'un ou l'autre:

  • Une méthode membre a n paramètres et l'autre a k paramètres, où n> = k. Les types des paramètres de la méthode du premier membre sont T1 ,. . . , Tn-1, tn [], les types des paramètres de l'autre méthode sont U1 ,. . . , UK-1, Royaume-Uni []. Si la deuxième méthode est générique, laissez R1 ... RP P1, ses paramètres de type formel, que BL soit la limite déclarée de RL, 1LP, Soit A1 ... AP les arguments de type réel déduits (§15.12.2.7) Pour cette invocation sous les contraintes initiales ti << ui, 1ik-1, ti << uk, kin et let si = ui [r1 = a1, ..., rp = ap] 1ik; Sinon, laissez si = ui, 1ik. Puis: pour tous j de 1 à k-1, tj <: sj, et, pour tous j de k à n, tj <: sk, et, si la deuxième méthode est une méthode générique comme décrit ci-dessus, alors al <: bl [R1 = a1, ..., rp = ap], 1lp.
  • Une méthode membre a k paramètres et l'autre a n paramètres, où n> = k. Les types des paramètres de la première méthode sont U1 ,. . . , UK-1, Royaume-Uni [], Les types des paramètres de l'autre méthode sont T1 ,. . ., Tn-1, tn []. Si la deuxième méthode est générique, laissez R1 ... RP P1, ses paramètres de type formel, que BL soit la limite déclarée de RL, 1LP, Soit A1 ... AP les arguments de type réel déduits (§15.12.2.7) Pour cette invocation sous les contraintes initiales, ui << ti, 1ik-1, uk << ti, kin et let si = ti [r1 = a1, ..., rp = ap] 1in; Sinon, laissez si = ti, 1in. Puis: pour tous j de 1 à k-1, uj <: sj, et, pour tous j de k à n, uk <: sj, et, si la deuxième méthode est une méthode générique comme décrit ci-dessus, alors al <: bl [R1 = a1, ..., rp = ap], 1lp.

Ignorer les génériques de problèmes, cela signifie-t-il que Varargs est plus important que le sous-typage, ou le sous-type est plus important que Varargs, lorsqu'il décide si une méthode est plus spécifique qu'un autre? Je ne peux pas comprendre.

Exemple concret: lequel des éléments suivants compute() Les méthodes sont «plus spécifiques» selon le JLS?

package com.example.test.reflect;

class JLS15Test
{
    int compute(Object o1, Object o2, Object... others) { return 1; }
    int compute(String s1, Object... others)            { return 2; }

    public static void main(String[] args) 
    {
        JLS15Test y = new JLS15Test();
        System.out.println(y.compute(y,y,y));
        System.out.println(y.compute("hi",y,y));
    }
}

Je ne peux pas comprendre lequel est "plus spécifique"; La sortie imprime

1
2

Je suis confus comment interpréter les résultats. Lorsque le premier argument était une chaîne, le compilateur a choisi la méthode avec le sous-type plus spécifique. Lorsque le premier argument était un objet, le compilateur a choisi la méthode avec le nombre moins de Varargs facultatifs.


REMARQUE: Si vous ne lisez pas cette section du JLS et que vous donnez une réponse qui dépend des types de arguments, vous ne m'aidez pas. Si vous lisez soigneusement le JLS, autre que les pièces relatives aux génériques, la définition de "plus spécifique" dépend des arguments déclarés, pas des arguments réels - Cela entre en jeu dans d'autres parties du JLS (je ne le trouve pas pour le moment).

par exemple pour les méthodes d'arity fixes, compute(String s) serait plus spécifique que compute(Object o). Mais j'essaie de comprendre la section pertinente des méthodes JLS RE: Arity variable.

Était-ce utile?

La solution 2

Après avoir lu le JLS plusieurs fois, je pense enfin que je comprends cette section.

Ce qu'ils disent, c'est que s'il existe deux méthodes variables de la carit . par exemple

int compute(Object o1, Object o2, Object... others)
int compute(String s1, Object... others)

peut être envisagé (uniquement pour que "plus spécifique") soit équivalent à

int compute(Object o1, Object o2, Object... others)
int compute(String s1, Object,    Object... others)

Et puis les types d'arguments sont comparés, un par un, cette dernière méthode étant plus spécifique.

(Plus rigoureusement, le premier a n = 3, k = 2, n> = k, avec String <: objet [String est un sous-type d'objet] et le JLS dicte la comparaison des types directement de chaque paramètre pour j entre 1 et K-1 [un de moins que la longueur plus courte], en comparant le type Vararg de la signature de la méthode plus courte avec les paramètres restants de la méthode plus longue.)

Dans le cas suivant:

int compute(Object o1, Object o2, String... strings)
int compute(Object o1, String... strings)

ceux-ci seraient équivalents (uniquement aux fins de "plus spécifiques") à

int compute(Object o1, Object o2, String... strings)
int compute(Object o1, String,    String... strings)

et ce dernier plus spécifique.

Donc, la variable-arity ne l'emporte jamais sur le sous-type dans le but de comparer des méthodes "plus spécifiques" qui sont toutes deux d'aripie variable.

Cependant, les méthodes à aire fixe sont toujours considérées en premier (JLS 15.12.2.2 et 15.12.2.3) avant les méthodes de variable-arity.

Autres conseils

  1. int compute(String s1, Object... others) est plus précis lorsque vous appelez compute("hi",y,y), puisque String est une sous-classe d'objet.

  2. int compute(Object o1, Object o2, Object... others) est le seulement faire correspondre compute(y,y,y) Parce que la deuxième méthode reçoit la chaîne comme premier param JLS15Test n'est pas une sous-classe de String

ÉDITER

Ma réponse réside sur les bases de méthodes spécifiques, mais votre code ne se compile qu'en raison de la capacité du compilateur à distinguer les méthodes de la manière décrite ci-dessus.

Les exemples suivants ne se compileront même pas en raison de son ambiguïté:

cas 1:

int compute(Object o1, Object o2, Object... others) { return 1; }
int compute(Object s1, Object... others)            { return 2; }

public static void main(String[] args) 
{
    JLS15Test y = new JLS15Test();
    System.out.println(y.compute(y,y,y));
    System.out.println(y.compute("hi",y,y));
}

Cas 2:

int compute(String o1, Object o2, Object... others) { return 1; }
int compute(Object s1, String... others)            { return 2; }

public static void main(String[] args) 
{
    JLS15Test y = new JLS15Test();
    System.out.println(y.compute("hi","hi","hi"));
}

Plus d'édition

Je n'ai pas bien compris votre question les deux premières fois (et j'espère que je le ferai cette fois :)).

Le cas réel dont vous parlez ressemblera à ça:

public class Test {
    public static void main(String[] args)
    {
        Test t = new Test();
        int a = t.compute("t", new Test());
        System.out.println(a);
    }

    int compute(String s, Object... others) { return 1; }
    int compute(Object s1, Object others)   { return 2; }
}

Dans ce cas, compute(Object s1, Object others) est en effet plus spécifique alors compute(String s, Object... others) (a moins de paramètres) donc la sortie sera 2 En effet.

Le deuxième appel de calcul imprime 2 parce que le "hi" littéral est connu pour être une chaîne au moment de la compilation, donc le compilateur choisit la deuxième signature de méthode car la chaîne est plus spécifique que l'objet.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top