2009-07-04 15 views
8

Tengo una clase C. La clase E la amplía.Pregunta sobre el polimorfismo de Java y la fundición

E e = new E(); 
C c = new C(); 

¿Por qué es

e = (E) c; 

Tras un examen más: aunque conversiones numéricas tienen la misma sintaxis que los objetos de fundición, surgió cierta confusión. En cualquier caso, lo anterior no da una compilación, sino más bien un error de tiempo de ejecución, por lo que una clase puede convertirse en subclase en algunos casos (de lo contrario, el código no se compilaría). ¿Hay algún ejemplo que alguien pueda dar donde funciona lo anterior?

Y también:

K extends M 

K k = new K(); 

((M) k).getClass() da K. ¿Porqué es eso? ¡Fue lanzado al más general M!

Supongamos que tengo un método DOIT() implementado en ambos M y K. ejecución

((M) k).doIt(); 

da DOIT de K de M o()?

Gracias!

Respuesta

14

Consideremos un ejemplo del mundo real:

public class Dog extends Animal 

Todos los perros son animales, pero no todos los animales son perros. Por lo tanto...

public class Cat extends Animal 

Lanzar un animal a un perro solo se puede realizar si el animal en cuestión es realmente un perro. De lo contrario, forzaría al Universo a inferir propiedades únicas a un perro (moviendo la cola, ladrando, etc.) hacia un Animal. Ese animal bien podría ser un gato con propiedades únicas (ronroneo, régimen riguroso de autolimpieza, etc.). Si el lanzamiento no es posible, se lanza una ClassCastException en el tiempo de ejecución.

Nadie quiere un perro que ronronee.


((M) k) .getClass() da K. ¿Por qué es esto? ¡Fue lanzado a la M más general!

Has lanzado k a M, pero todas las clases tienen un método getClass(). La clase de k siempre es K, independientemente de a qué referencia le hagas o no M. Si lanzas un perro a un animal y le preguntas qué animal es, todavía responderá que es un perro.

De hecho, la conversión a una superclase es redundante. Un Perro ya es un Animal y tiene todos los métodos de un Animal además del propio. Muchas herramientas de Análisis de Código, como FindBugs, le notificarán de conversiones redundantes para que pueda eliminarlas.


Supongamos que tengo un método DOIT() implementado en ambos M y K. ejecución

((M) k) .doIt();

da doIt de M o K()?

K doIt() por las mismas razones que las anteriores. El yeso opera en la referencia; no transforma un objeto en un tipo diferente.


¿Puede dar un ejemplo de cuando la fundición (= perro (perro) myAnimal perro) tiene sentido?

Sure can. Imagine un método que recibe una lista de animales para su procesamiento. Todos los perros deben ser llevados a caminar, y todos los gatos deben jugarse con un juguete en forma de pájaro. Para hacer esto, llamamos al método takeForWalk() que solo existe en Dog, o al método play() que solo existe en Cat.

public void amuseAnimals(List<Animal> animals) { 
    for (Animal animal : animals) { 
     if (animal instanceof Dog) { 
      Dog doggy = (Dog)animal; 
      doggy.takeForWalk(new WalkingRoute()); 
     } else if (animal instanceof Cat) { 
      Cat puss = (Cat)animal; 
      puss.play(new BirdShapedToy()); 
     } 
    } 
} 
+0

¿Por qué ejecuta K doIt? Le digo que se refiera a este objeto como M, no como K. Si el compilador lo trata como M, debería ejecutar los métodos de M. Estoy aún más confundido ahora. ¿Puedes dar un ejemplo de cuando lanzaste (Dog doggy = (Dog) myAnimal) tiene sentido? – ooboo

+0

"Si el compilador lo trata como una M, debería ejecutar los métodos de M.". El compilador lo trata como una M por razones de tipeo. Si K no se puede escribir como M, se emite una ClassCastException. Se usan los métodos de K porque como una subclase K puede anular los métodos de M. Esto es lo que separa una K de una M y es una parte intrínseca de la Orientación del objeto. – banjollity

7

El hecho de que E se extiende C, C no se convierta en un E ... E por el contrario es un C

Editar: Para ampliar el comentario de Marcos a continuación ... El hecho de que cada mujer es una humano, no todos los humanos somos mujeres. Todos los seres humanos comparten la "interfaz humana" con las piernas, las manos, las caras, etc. Las mujeres la amplían con una funcionalidad que les devuelve buenos sentimientos cuando les regalan diamantes y oro.

La conversión int => double ni siquiera está relacionada, ya que no es un molde de clase sino una conversión que le dice al compilador que almacene lo que está en x en y (que resulta ser un doble).

((M) k) .getClass() da K.

porque k es todavía un K incluso si lo lanzas a un M o un objeto (o alguna otra cosa que pasa a ser)

Edit: Creo que la confusión aquí es porque consideras k "convertirse" en M cuando lo lanzas, no es así. Lo está tratando como un M. Si le pregunta a alguien que es "dueño de un perro" qué clase de raza es él no devolverá "Es un perro", la razón es simplemente que el método getBreedName() es probable que han sido anulados en la subclase LabradorOwner para devolver "Labrador". Es lo mismo con getClass(), devolverá la clase de la implementación. No será una M sino una K que también es una M simplemente porque K extiende M.

+0

¿Por qué no? Cada E es una C. Entonces, ¿por qué no puedo tratar un objeto C como una E? Para la segunda parte, ¿qué hace el casting? No cambia la variable. ¿Qué sucede si ejecuto ((M) k) .doIt() - se ejecutará K's o M doIt()? – ooboo

+6

También podría preguntar: cada mujer es una persona; ¿Por qué no puedo tratar a cada persona como una mujer? Respuesta: puedes **, pero primero debes * verificar * que son mujeres. –

+2

Cada E es una C pero no todas las C son E. E extiende C es como decir E agrega a C, por lo tanto, una C puede no tener la misma cantidad de cosas que una E. –

4

int/double no está relacionado; eso es una conversión, no un reparto, no hay relación entre int y double.

Re la pregunta; el objeto de un tipo se fija en la creación. Un objeto que es un Cno es (y nunca puede ser) un E.Sin embargo, puede tratar como E como C, ya que la herencia representa "es una". Por ejemplo:

E e = new E(); 
C c = e; 

Aquí todavía sólo tenemos un objeto - simplemente que la variable c piensa en él como un C, por lo que no expondrá métodos específicos para E (a pesar de que el objeto es un E) .

Si añadimos a continuación:

E secondE = (E) c; 

Esta es una verificación de tipos; de nuevo, no hemos cambiado el objeto, pero para poner c en una variable E se requiere que demostremos al compilador/tiempo de ejecución que es realmente y E. No necesitamos esto en el primer ejemplo, ya que puede probar que cualquier E también es C.

Del mismo modo, con el getClass() - todo lo que hace el molde es cambiar la forma en que el compilador piensa en el objeto; no has cambiado el objeto en sí. Todavía es un K.

Necesita separar las variables de objetos. El elenco está hablando de las variables ; ellos no cambian el objeto.

+0

Gracias. ¿Puedes dar un ejemplo donde E secondE = (E) c; ¿trabajaría? – ooboo

+0

Él acaba de hacer ... cuando lo hace: E e = new E(); C c = e y luego hacer una E de nuevo con "E secondE = (E) c"; Debido a que el objeto ha sido una E todo el tiempo (incluso si alguien lo trata como C es por herencia), la tercera fila será perfectamente legal. – Fredrik

+1

En el código real por cierto, la tercera línea probablemente estaría dentro de alguna comprobación "if (c instanceof E) ...Si hubieras sabido todo el tiempo que tu c es una E, no tendría sentido lanzarla a una C en la línea dos. – Fredrik

0

Para la primera pregunta, no se puede convertir una superclase en una subclase porque una subclase agrega miembros que la superclase no tiene. ¿Cómo se supone que el compilador sabe qué valores poner allí cuando está emitiendo? Básicamente, una E es una C, pero una C no es una E.

getClass() obtiene el tipo de objeto en la memoria. Casting a M simplemente oculta el hecho de que es una K, no cambia el objeto subyacente.

8

No puede lanzar objetos en Java.

Puede moldear referencias en Java.

Lanzar una referencia no cambia nada sobre el objeto al que se refiere. Solo produce una referencia de un tipo diferente que apunta al mismo objeto que la referencia inicial.

Los valores primitivos de fundición son diferentes de las referencias de fundición. En este caso, los valores cambian.

1

Para agregar a la respuesta de Frederik, lanzar un objeto a algo no cambia su tipo. También, objeto de solamente se puede convertir a un tipo que ya es (el compilador no sabe en ese momento) Por eso nunca serán aceptadas moldes imposibles:

Integer i = (Integer) new String(); 

no se compilará, porque el compilador sabe no puede ser posible

1

((M) k) .getClass() da K. ¿Por qué es esto? ¡Fue lanzado a la M más general!

Una analogía útil (que obtuve de artima.com sitio de Bill Venners') que pueden ayudar a aclarar la confusión es que la diferencia entre las clases y los objetos es como la diferencia entre el modelo de un arquitecto y la casa real que es construido. El plan existe en papel y es un concepto, mientras que la casa existe en la vida real. Puede tener más de una casa construida con el mismo modelo.

¿Qué relación tiene esto con esta pregunta? Digamos que hay un modelo McMansion y un plan McMansionWithHeatedPool. A McMansionWithHeatedPool es una extensión de McMansion con una piscina climatizada.

Ahora, si usted ve un verdadero McMansionWithHeatedPool, para ese objeto:

  1. Conceptualmente (es decir, si nos fijamos en los planos del arquitecto), se vería que un McMansionWithHeatedPool es claramente también un McMansion. Por lo tanto, la subida está permitida. (Por la misma razón, un objeto McMansion no puede ser echado en un tipo McMansionWithHeatedPool: no hay piscina climatizada!)

  2. ((McMansion) k) .getClass() da McMansionWithHeatedPool porque k es todavía un McMansionWithHeatedPool. El tipo de conversión está en la expresión, no en el objeto.

1

"Si el compilador lo trata como un M, se debe ejecutar métodos de M".
El compilador trata la referencia como M. El ejemplo que los puntos de referencia a es de tipo K, no M. No se puede echar la referencia y asumir que significa la instancia cambiará repentinamente comportamiento. Lo que hace el compilador es asegurarse de que exista el método invocado en la referencia especificada. No tiene nada que ver con la implementación que se invoca, solo que existe una implementación.

0

Lanzar un objeto no cambia el objeto al objeto que se está emitiendo, pero permite otra referencia de clase que está relacionada con él por herencia para hacer referencia al objeto.

Por ejemplo C extends E. Y ambos tienen un método myName();. Si usted dice

E e = new C(); 
e.myName(); 

que está llamando C myName() método y si también responde

E e = new E(); 
C c = (C)e; 

que acaba de decir el compilador que se debe permitir que se refieren a E con C tipo de referencia.

Cuestiones relacionadas