¿Por qué este método Java es polimorfo por tipo declarado y no de tiempo de ejecución?

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

  •  26-10-2019
  •  | 
  •  

Pregunta

Este código:

public class PMTest
{

    private static class Runner { }
    private static class Server extends Runner { }

    private static class Task
    {
        public void delegate(Runner runner)
        {
            System.out.println("Task: " + runner.getClass().getName() +
                               " / " + this.getClass().getName());
        }
    }

    private static class Action extends Task
    {
        public void delegate(Server server)
        {
            System.out.println("Action: " + server.getClass().getName() +
                               " / " + this.getClass().getName());
        }
    }


    private static void foo(Task task, Runner runner)
    {
            task.delegate(runner);
    }

    private static void bar(Action task, Runner runner)
    {
            task.delegate(runner);
    }

    private static void baz(Action task, Server runner)
    {
            task.delegate(runner);
    }


    public static void main (String[] args)
    {
        try {
            Server server = new Server();
            Action action = new Action();

            action.delegate(server);
            foo(action, server);
            bar(action, server);
            baz(action, server);

        }
        catch (Throwable t) {
            t.printStackTrace();
        }
    }
}

produce esta salida:

$ java PMTest
Action: PMTest$Server / PMTest$Action
Task: PMTest$Server / PMTest$Action
Task: PMTest$Server / PMTest$Action
Action: PMTest$Server / PMTest$Action

Puedo ver muy claramente que el método de la tarea se elige sobre el método de Action. Sin embargo, no entiendo por qué, ya que los objetos siempre saben cuáles son y pensé que la selección del método de unión tardía de Java podría distinguir la diferencia en las firmas de métodos. La llamada a bar() es especialmente confuso, como task se declara como un Action en ese punto.

Si hace la diferencia, este es Java 6:

$ java -version
java version "1.6.0_14"
Java(TM) SE Runtime Environment (build 1.6.0_14-b08)
BEA JRockit(R) (build R27.6.5-32_o-121899-1.6.0_14-20091001-2113-linux-ia32, compiled mode)

Puedo cambiar mi código para que funcione, pero me gustaría entender por qué no funciona. ¡Gracias por la ayuda!

¿Fue útil?

Solución

Así es como funciona el despacho en Java.

El despacho se basa primero en los tipos de parámetros estáticos, y una vez que se ha elegido una firma estática, entonces el tipo de ejecución del objeto que contiene el método se utiliza para determinar qué primordial se utiliza.

Por ejemplo, en

void foo(Object o) {
  if (o instanceof Number) { foo((Number) o); }
  else if (o instanceof String) { foo((String) o); }
}

void foo(String s) { ... }

void foo(Number n) { ... }

{ foo((Object) "foo"); }  // Calls foo(Object) which calls foo(String).
{ foo("foo"); }  // Calls foo(String) without first calling foo(Object).

Otros consejos

La elección entre métodos sobrecargados siempre se realiza en el momento de la compilación, nunca en tiempo de ejecución. El polimorfismo de tiempo de ejecución implica elegir entre métodos que anulen otros métodos, es decir, métodos con firmas idénticas.

Porque una vez que pasas Server dentro foo o bar, en el alcance de esa llamada no es un Server sino más bien un Runner.

Por lo tanto, cuando corres delegate Se unirá a la coincidencia más apropiada, de acuerdo con la firma del método. delegate(Runner) no requiere la peligrosa negligencia del parámetro alcanzado en un Server.

Tenga en cuenta que este alcance no se realiza en tiempo de ejecución, también mantendrá el análisis estático del código fuente. Es solo que recuerdas que el servidor era un Server Eso te confunde. Si analiza el código sin ese conocimiento adicional fuera del alcance, verá que delegate(Runner) Realmente es solo su elección válida.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top