103 votes

Surcharge de la méthode de sélection basée sur le paramètre de type réel

Je suis en train d'expérimenter avec ce code:

interface Callee {
    public void foo(Object o);
    public void foo(String s);
    public void foo(Integer i);
}

class CalleeImpl implements Callee
    public void foo(Object o) {
        logger.debug("foo(Object o)");
    }

    public void foo(String s) {
        logger.debug("foo(\"" + s + "\")");
    }

    public void foo(Integer i) {
        logger.debug("foo(" + i + ")");
    }
}

Callee callee = new CalleeImpl();

Object i = new Integer(12);
Object s = "foobar";
Object o = new Object();

callee.foo(i);
callee.foo(s);
callee.foo(o);

Ce imprime foo(Object o) trois fois. Je m'attends à la méthode de sélection à prendre en compte le réel (et non déclarées) type de paramètre. Ai-je raté quelque chose? Est-il un moyen de modifier ce code afin qu'il imprimer foo(12), foo("foobar") et foo(Object o)?

86voto

Michael Borgwardt Points 181658

Je m'attends à la méthode de sélection à prendre en contrepartie le réel (et non le déclarée) type de paramètre. Ai-je raté quelque chose?

Oui. Votre attente est mauvais. En Java, la dynamique de la méthode de répartition ne se produit que pour l'objet, la méthode est appelée, non pas pour les types de paramètres de méthodes surchargées.

Citant le Java Language Specification:

Lorsqu'une méthode est appelée (§15.12), le nombre d'arguments (et de toute explicite le type des arguments) et la au moment de la compilation types d'arguments sont utilisés, au moment de la compilation, à déterminer la signature de la méthode qui sera invoquée (§15.12.2). Si la méthode qui sera invoquée est un méthode d'instance, la méthode de être invoquée sera déterminée lors de l'exécution de temps, en utilisant la méthode dynamique de recherche (§15.12.4).

80voto

denis.zhdanov Points 1669

Comme mentionné précédemment, la surcharge de résolution est effectuée au moment de la compilation.

Java casse-têtes est un bel exemple:

Puzzle 46: Le Cas de la Confusion de Constructeur

Ce puzzle, vous présente deux de Confusion des constructeurs. La principale méthode appelle un constructeur, mais lequel? Le programme de sortie dépend de la réponse. Quel est le programme d'impression, ou est-il même légal?

public class Confusing {

    private Confusing(Object o) {
        System.out.println("Object");
    }

    private Confusing(double[] dArray) {
        System.out.println("double array");
    }

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

Solution 46: le Cas de la Confusion Constructeur

... Java de la résolution de surcharge processus fonctionne dans les deux phases. La première phase permet de sélectionner toutes les méthodes ou les constructeurs qui sont accessibles et applicables. La deuxième phase sélectionne les plus spécifiques des méthodes ou des constructeurs sélectionnés dans la première phase. Une méthode ou d'un constructeur est moins précis qu'un autre s'il peut accepter des paramètres passés à l'autre [JLS 15.12.2.5].

Dans notre programme, les deux constructeurs sont accessibles et applicables. Le constructeur La confusion(Objet) accepte un paramètre passé à la Confusion(double[]), de sorte La confusion(Objet) est moins précis. (Chaque double tableau est un Objet, mais pas tous l' Objet est un double tableau.) Le plus spécifique constructeur est donc source de Confusion(double[]), ce qui explique le programme de la sortie.

Ce comportement a de sens que si vous passez une valeur de type double[]; il est contre-intuitif si vous passez null. La clé de la compréhension de ce puzzle, c'est que le test de la méthode ou le constructeur est plus spécifique n'utilise pas les paramètres: les paramètres figurant dans l'invocation. Ils sont uniquement utilisés pour déterminer les surcharges sont applicables. Une fois que le compilateur détermine les surcharges sont applicables et accessible, il choisit le plus spécifique de la surcharge, en utilisant uniquement les paramètres formels: les paramètres figurant dans la déclaration.

Pour appeler la Confusion(Objet) constructeur avec un null paramètre, écrire de nouveaux La confusion((Objet)null). Cela garantit que seule la Confusion(Objet) est applicable. Plus généralement, pour forcer le compilateur de choisir une surcharge, la fonte de paramètres à l'déclaré types des paramètres formels.

15voto

Anton Gogolev Points 59794

Capacité d'envoi d'un appel à une méthode basée sur les types d'arguments est appelé la répartition multiple. En Java, cela se fait avec des habitudes des Visiteurs.

Cependant, puisque vous avez à traiter avec des Integers et Strings, vous ne pouvez pas facilement intégrer ce modèle (vous ne pouvez pas modifier ces classes). Ainsi, un géant de l' switch sur l'objet au moment de l'exécution sera votre arme de choix.

11voto

Yishai Points 42417

En Java de la méthode à appeler (comme dans la méthode de signature à utiliser) est déterminée au moment de la compilation, il va avec le moment de la compilation type.

Le modèle typique pour travailler autour de cela est de vérifier le type d'objet dans la méthode avec l'Objet de la signature et de délégué à la méthode avec un cast.

    public void foo(Object o) {
        if (o instanceof String) foo((String) o);
        if (o instanceof Integer) foo((Integer) o);
        logger.debug("foo(Object o)");
    }

Si vous avez de nombreux types et c'est ingérable, puis la surcharge de méthode est probablement pas la bonne approche, mais plutôt la méthode publique doit juste prendre de l'Objet et de mettre en œuvre une sorte de modèle de stratégie de déléguer la gestion appropriée par type d'objet.

4voto

Alex Worden Points 1099

J'ai eu un problème similaire avec l'appel de la droite constructeur d'une classe appelée "Paramètre" qui peut prendre plusieurs les types Java de base tels que String, Integer, Boolean, de temps, etc. Étant donné un tableau d'Objets, je veux les convertir en un tableau de mes objets de Paramètre par l'appel de la plus spécifique au constructeur pour chaque Objet du tableau d'entrée. Je voulais également définir le Paramètre de constructeur(Object o) qui lancerait une IllegalArgumentException. J'ai bien sûr trouvé cette méthode invoquée pour chaque Objet dans mon tableau.

La solution que j'ai utilisé a été de chercher le constructeur via la réflexion...

public Parameter[] convertObjectsToParameters(Object[] objArray) {
    Parameter[] paramArray = new Parameter[objArray.length];
    int i = 0;
    for (Object obj : objArray) {
        try {
            Constructor<Parameter> cons = Parameter.class.getConstructor(obj.getClass());
            paramArray[i++] = cons.newInstance(obj);
        } catch (Exception e) {
            throw new IllegalArgumentException("This method can't handle objects of type: " + obj.getClass(), e);
        }
    }
    return paramArray;
}

Pas laid instanceof, les instructions switch, ou des habitudes des visiteurs nécessaire! :)

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X