2009-01-01 9 views
14
public class CovariantTest { 
    public A getObj() { 
     return new A(); 
    } 

    public static void main(String[] args) { 
     CovariantTest c = new SubCovariantTest(); 
     System.out.println(c.getObj().x); 
    } 
} 

class SubCovariantTest extends CovariantTest { 
    public B getObj() { 
     return new B(); 
    } 
} 

class A { 
    int x = 5; 
} 

class B extends A { 
    int x = 6; 
} 

El código anterior imprime 5 cuando se compila y se ejecuta. Utiliza el retorno covariante para el método over-ridden.Java Covariantes

¿Por qué imprime 5 en lugar de 6, ya que ejecuta el método getObj en la clase SubCovariantTest?

¿Puede alguien arrojar algo de luz sobre esto. Gracias.

Respuesta

15

Esto se debe a que en las variables miembro de Java no se anulan, sombra (a diferencia de los métodos). Ambos A y B tienen una variable x. Como c es declarado como de tipo CovarientTest, el retorno de getObj() es implícitamente una A, no B, por lo que se obtiene A x, no B x.

11

Java no anula los campos (también conocidos como atributos o variables de miembro). En su lugar, se shadow over entre sí. Si ejecuta el programa a través del depurador, encontrará dos variables x en cualquier objeto que sea del tipo B.

Aquí hay una explicación de lo que está sucediendo. El programa primero recupera algo que es implícitamente del tipo A y luego llama al x que se supone que proviene de A. Aunque es claramente un subtipo, en su ejemplo se crea un objeto de tipo B a través de SubCovariantTest, aún asume que debe devolver algo en getObj() que está implícitamente escrito A. Dado que Java no puede anular campos, la prueba llamará al A.x y no B.x.

CovariantTest c = new SubCovariantTest(); 
// c is assumed the type of CovariantTest as it is 
// implicitly declared 

System.out.println(c.getObj().x); 
// In this method chain the following happens: 

// c.getObj() will return object of type B 
// BUT will assume it is an A 

// c.getObj().x will return the x from A 
// since in this context the compiler assumes 
// it is an A and make the call to A.x 

Parece una Gotcha mindboggling porque los métodos siempre se redefinen en Java (en comparación con C++ y C# en los que no lo son). Por lo general, no se encontrará con este problema porque la convención del código Java le indica que nunca debe acceder directamente a los campos. En su lugar, asegúrese de que los campos siempre se accede a través de métodos de acceso, es decir captadores:

class A { 
    private int x = 5; 

    public int getX() { // <-- This is a typical accessor method 
     return x; 
    } 
} 

class B extends A { 
    private int x = 6; 

    @override 
    public int getX() { 
     // will be called instead even though B is implied to be A 
     // @override is optional because methods in Java are always virtual 
     // thus are always overridden 
     return x; 
    } 
} 

código para conseguir este trabajo es la siguiente:

c.getObj().getX(); 
// Will now call getX() in B and return the x that is defined in B's context. 
10

reemplazar sus A y B anteriores con:

class A { 
    public int getX() { return 5; } 
} 

class B extends A { 
    public int getX() { return 6; } 
} 

Eso probablemente responda a su pregunta sobre lo que está mal ;-)

9

Hay dos campos llamados x en el objeto, uno de clase A y uno de clase B, que oculta el de A. El campo x al que se hace referencia es el de A, debido a la declaración de c .

En la práctica esto no es un problema, porque es muy mal estilo de

  • ocultar un campo en una subclase,

  • acceso a un campo directamente en lugar de a través de un método.

1

Estaba viendo su código y estaba teniendo problemas para compilarlo. Obteniendo el error

El tipo de devolución no es compatible con CovariantTest.getObj()

Hice un pequeño cambio.

class A { 
    int x = 5; 
} 

class B extends A { 
    int x = 6; 
} 

public class CovariantTest { 

    public A getObj() { 
     return new A(); 
    } 

    public static void main(String[] args) { 
     CovariantTest c = new SubCovariantTest(); 
     A a = c.getObj(); 

     System.out.println(c.getObj().x); 
    } 
} 

class SubCovariantTest extends CovariantTest { 
    public A getObj() { 
     return new B(); 
    } 

} 

Coloque un punto de interrupción en la línea de salida del sistema y observe la variable a. Contiene dos miembros x, con un conjunto de 5 y uno a 6.

La respuesta de Starblue explica este comportamiento.

0

c se escribe como CovariantTest en tiempo de compilación y, por lo tanto, la llamada a c.getObj() está vinculada al método CovariantTest.getObj() en tiempo de compilación (y no puede modificarse en tiempo de ejecución).

Además, x existe tanto en A como en B (se sombrea, no se reemplaza). Debido a que el método que se llama es CovariantTest.getObj() y que el método funciona con A, la x es Ax ser recuperado a pesar de que el objeto real es de tipo B.

2
package ch2; 

class CovariantTest 
{ 
    public A getObj() 
    { 
     return new A(); 
    } 
} 

class SubCovariantTest extends CovariantTest 
{ 
    public B getObj() 
    { 
     return new B(); 
    } 
} 

public class TestPrg 
{ 
    public static void main(String[] args) 
    { 
     CovariantTest c = new SubCovariantTest(); 
     System.out.println("c.getObj().x :: "+c.getObj().x); 
     System.out.println("c.getObj().getX() :: "+c.getObj().getX()); 
    } 
} 

class A 
{ 
    int x = 5; 

    int getX() 
    { 
     return x; 
    } 
} 

class B extends A 
{ 
    int x = 6; 

    int getX() 
    { 
     return x; 
    } 
} 

simple ... El polimorfismo es solamente aplicable para funciones. No variables

Las variables se resolverán durante el tiempo de compilación.

Cuestiones relacionadas