2012-02-24 14 views
29

Por this answer y this answer, los métodos estáticos de Java no son virtuales y no pueden anularse. Intuitivamente, por lo tanto, esto debería funcionar (aunque en el 99% de los casos es la programación peligroso):¿Por qué Java exige la compatibilidad del tipo de devolución para los métodos estáticos anulados?

class Foo 
{ 
    public static String frob() { 
     return "Foo"; 
    } 
} 

class Bar extends Foo 
{ 
    public static Number frob() { 
     return 123; 
    } 
} 

Sin embargo, en la práctica esto se obtiene:

Foo.java:10: frob() in Bar cannot override frob() in Foo; attempting to use incompatible return type 
found : java.lang.Number 
required: java.lang.String 
    public static Number frob() { 
         ^

Ingenuamente, parece que Foo.frob() y Bar.frob() no deberían tener nada que ver el uno con el otro; sin embargo, Java insiste en que lo hagan. ¿Por qué?

(Nota:. No quiero saber por qué sería una mala idea para codificar esta manera, quiero oír lo que es en Java y/o el diseño JVM que hace que esta restricción es necesario)


actualizado para añadir: para aquellos que piensan que el compilador va a confundirse con la llamada a métodos estáticos en los casos, si usted permite esto: no lo hará. Ya se tiene que resolver esto en el caso en que las firmas de los métodos son compatibles:

class Foo 
{ 
    static String frob() { 
     return "Foo"; 
    } 
} 

class Bar extends Foo 
{ 
    static String frob() { 
     return "Bar"; 
    } 
} 

class Qux { 
    public static void main(String[] args) { 
     Foo f = new Foo(); 
     Foo b = new Bar(); 
     Bar b2 = new Bar(); 

     System.out.println(f.frob()); 
     System.out.println(b.frob()); 
     System.out.println(b2.frob()); 
    } 
} 

se obtiene:

Foo 
Foo 
Bar 

La pregunta es, ¿cuál es la razón concreta por la que no pudo mientras fácilmente (en el caso incompatibles firmas) conseguir que:

Foo 
Foo 
123 

Respuesta

8

considerar lo siguiente:

public class Foo { 
    static class A { 
    public static void doThing() { 
     System.out.println("the thing"); 
    } 
    } 

    static class B extends A { 

    } 

    static class C extends B { 
    public static void doThing() { 
     System.out.println("other thing"); 
    } 
    } 

    public static void main(String[] args) { 
    A.doThing(); 
    B.doThing(); 
    C.doThing(); 
    } 
} 

Run it! Se compila e imprime

the thing 
the thing 
other thing 

Los métodos estáticos tipo de heredan - en el sentido de que B.doThing se traduce en una llamada a A.doThing - y en cierto modo se puede anular.

Parece que fue principalmente una decisión de juicio para el JLS. Sin embargo, la manera más específica en que el JLS parece resolver esto es section 8.2, que simplemente no dice que los métodos estáticos no son heredados.

+0

Woah, amigo, hombre. – Tom

+0

No estoy seguro de que esta haya sido necesariamente la mejor decisión posible: dio lugar a torpezas como [este código en Guava] (http://code.google.com/p/guava-libraries/source/browse/guava/ src/com/google/common/collect/ImmutableSortedSetFauxverideShim.java) - pero no es una decisión irracional, y supongo que tenían razones para esta decisión que yo todavía no había pensado. –

+2

'myB' llamará a' doThing() 'fuera del tipo' myB'. –

2

Es porque en Java, se llama a un método particular en función del tipo de tiempo de ejecución del objeto y no del tipo de tiempo de compilación del mismo. Sin embargo, los métodos estáticos son métodos de clase y, por lo tanto, el acceso a ellos siempre se resuelve durante el tiempo de compilación utilizando la información del tipo de tiempo de compilación. Es decir, ¿qué pasaría si se pudiera compilar el código de seguridad y el uso como esto

Foo bar = new Bar(); 
bar.frob(); 
+2

Esto no responde la pregunta. En su ejemplo, esperaría que 'Foo.frob()' sea llamado como 'bar' es un' Foo'. –

+0

@SteveKuo Pero las reglas de firma Java normales entran para jugar en este caso; los métodos heredados no pueden tener los mismos parámetros y diferentes tipos de devolución. –

3

JLS 8.4.2 Method Signature, brevemente:

dos métodos tienen la misma firma si tienen el mismo nombre y argumentos tipos.

No se dice nada sobre la estática.Los métodos estáticos se pueden llamar a través de una instancia (o una referencia nula): ¿cómo debe resolverse el método si se hace referencia a una subclase a través de una declaración de superclase?

+2

El compilador ya es capaz de resolver esto - vea mi comentario en [Respuesta de Ryan Shillington] (http://stackoverflow.com/a/9439532/27358). –

+1

@DavidMoles Entonces es simplemente "porque Java no permite que los métodos tengan la misma firma pero diferentes tipos de devolución". En cuanto a lo racional, tendrías que preguntarle a Gosling/etc. pero la mayor parte se resuelve como "es una manera fácil de eliminar un tipo particular de error de usuario". –

1

Bueno, la JVM probablemente podría estar hecha para permitir eso, pero hablemos de por qué es una muy mala idea desde la perspectiva del compilador.

La información de la instancia (incluido el tipo de la instancia) no es algo que sea un problema simple de resolver en tiempo de compilación. Obviamente es bien conocido en tiempo de ejecución. Si tengo una barra variable de tipo Bar, y llamo a s = bar.frob(), el compilador necesitaría realizar una ingeniería inversa de qué tipo es la barra para ver si el valor de retorno es aceptable. Si la determinación del tipo en el momento de la compilación es un problema súper difícil, esto hace que el compilador sea ineficiente en el mejor de los casos. En el peor de los casos, la respuesta es incorrecta y se obtienen errores de tiempo de ejecución que deberían haberse capturado en tiempo de compilación.

+3

No compro este argumento. El compilador ya tiene una regla para esto. Si 'b' se declara como' Bar', entonces 'b.frob()' es 'Bar.frob()', y si 'b' se declara como' Foo', incluso si 'b' es * en realidad * una instancia de 'Bar',' b.frob() 'es' Foo.frob() '. Esto es fácil de demostrar si crea un ejemplo donde los tipos son compatibles. –

+0

(La pregunta se ha editado para agregar un ejemplo). –

+0

En lo que respecta a la JVM, buscará en la jerarquía de herencia una coincidencia exacta, incluido el tipo de devolución. Ignora los "tipos de retorno covariante" y similares. –

Cuestiones relacionadas