Ejemplos de código - Orientación a Objetos

A continuación dejo unos ejemplos de código con explicaciones. Estos ejemplos surgieron a partir de la realización de los ejercicios del capítulo 2 (Object Orientation) del libro "Sun Certified Programmer for Java 6 Study Guide".
Para ver el post con el resumen de dicho capítulo haz click aquí.
Cada ejemplo tiene un link para visualizar el código original desde el proyecto (SVN) google.

EJEMPLO 1 (Ver código original)

01. class Dog {
02. public void bark() {
03. System.out.print("woof ");
04. }
05. }
06.
07. class Hound extends Dog {
08. public void sniff() {
09. System.out.print("sniff ");
10. }
11. public void bark() {
12. System.out.print("how1 ");
13. }
14. }
15.
16. public class DogShow {
17. public static void main(String[] args) {
18. new DogShow().go();
19. }
20. void go() {
21. new Hound().bark();
22. ((Dog) new Hound()).bark();
23. ((Dog) new Hound()).sniff();
24. }
25. }
/*
DogShow.java:24: cannot find symbol
symbol : method sniff()
location: class Dog
((Dog) new Hound()).sniff();
^
1 error
*/


El casteo explícito (línea 23) provoca que el compilador tome la expresión ((Dog) new Hound()) como de tipo Dog. Como la clase Dog no contiene un método sniff(), se genera el error de compilación "cannot find symbol".
Si no se haría la generalización al tipo Dog, o se agregará una especialización al tipo Hound como la siguiente: ((Hound) ((Dog) new Hound())).sniff(); el programa compilaría y se ejecutaría correctamente.
Por otro lado, al comentar la línea 23 de este ejemplo, la salida sería la siguiente: how1 how1. Esto demuestra que más allá de que se realice la generalización de la línea 22, se invoca la versión del método bark() de la clase Hound. Esto se debe a que la selección de las versiones de los distintos métodos sobreescritos, se hace en tiempo de ejecución, y depende del tipo de objeto instanciado.
En conclusión llegamos a que el código original de este ejemplo, en tiempo de ejecución funcionaría correctamente (porque se invocaría al método sniff() de un objeto Hound). Pero como el compilador no encuentra la declaración del método sniff() en la clase padre,
genera un error de compilación. (Para comprobar lo dicho anteriormente se podría declarar un método sniff() con cualquier cuerpo en la clase Dog, el cuál en tiempo de ejcución nunca sería invocado por este ejemplo.)

01. class Dog {
02. public void bark() {
03. System.out.print("woof ");
04. }
05. public void sniff() {
06. System.out.print("sniff Dog");
07. }
08. }



EJEMPLO 2 (Ver código original)

01. public class Redwood extends Tree {
02. public static void main(String[] args) {
03. new Redwood().go();
04. }
05. void go() {
06. go2(new Tree(), new Redwood());
07. go2((Redwood) new Tree(), new Redwood());
08. }
09. void go2(Tree t1, Redwood r1) {
10. Redwood r2 = (Redwood)t1;
11. Tree t2 = (Tree)r1;
12. }
13. }
14. class Tree {}
/*
SALIDA:
Exception in thread "main" java.lang.ClassCastException: Tree cannot be cast to
Redwood
at Redwood.go2(Redwood.java:10)
at Redwood.go(Redwood.java:6)
at Redwood.main(Redwood.java:3)
*/

Si comentáramos la línea 10 (la cuál provoca la excepción ClassCastException en tiempo de ejecución) y luego ejecutaríamos el programa, obtendríamos la siguiente SALIDA:
Exception in thread "main" java.lang.ClassCastException: Tree cannot be cast to
Redwood
at Redwood.go(Redwood.java:7)
at Redwood.main(Redwood.java:3)

Esto demuestra que el casteo (o especialización) de un objeto Tree hacia Redwood generá una excepción en tiempo de ejecución.
Entonces, se puede concluir que NO se puede realizar una especialización de un tipo base a un subtipo. Es decir, si tenemos una variable de referencia a un subtipo e intentamos asignarle un supertipo obtendremos un error (en este caso de compilación "incompatible types").
Lo mismo sucedería (como lo demuestra este ejemplo) al castear un supertipo hacia un subtipo (pero en este caso es un error un tiempo de ejecución).

EJEMPLO 3 (Ver código original)

01. class A {}
02. class B extends A {}
03. public class ComingThru {
04. static String s = "-";
05. public static void main(String[] args) {
06. A[] aa = new A[2];
07. B[] ba = new B[2];
08. sifter(aa);
09. sifter(ba);
10. sifter(7);
11. System.out.println(s);
12. }
13. static void sifter(A[]... a2) { s += "1"; }
14. static void sifter(B[]... b1) { s += "2"; }
15. static void sifter(B[] b1) { s += "3"; }
16. static void sifter(Object o) { s += "4"; }
17. }
/*
SALIDA:
-434
*/

En general, los métodos sobrecargados con argumentos var-arg son elegidos últimos (para ser ejecutados). Cabe destacar que los arrays son objetos, por este motivo la invocación de la línea 8 ejecuta la versión del método con el parámetro Object.
Entonces se podría decir que ciertos métodos tienen mayores posibilidades de ser ejecutados:
  1. Parámetros con el tipo idéntico (como el caso de la invocación de la línea 9).
  2. Parámetros con el supertipo u Object (como el caso de las invocaciones de las líneas 8 y 10).
  3. Por último si los argumentos no concuerdan con ningún otro método, se ejecutan los métodos con parámetros var-arg.

0 comentarios:



Publicar un comentario en la entrada

Este blog dejo de ser mantenido el día 12/02/2010. Para cualquier consulta o comentario realizarlo a través del sitio http://onj2ee.blogspot.com/