POO en lenguajes compilados de tipos estáticos (Java y C#) Carlos Fontela cfontela@fi.uba.ar A3F Temario Modelo de referencias, creación y destrucción de objetos (introducción) Paquetes y grupos de clases Constructores Excepciones: captura y lanzamiento Visibilidad Herencia y transformaciones de tipos Polimorfismo básico (basado en herencia) 2c2009 2 A3F Creación de objetos Declaración: ArrayList x; Definición: x = new ArrayList( ); Significado ArrayList es la clase de x ArrayList( ) es el “constructor” de la clase ArrayList El objeto se crea recién cuando llamo al constructor con el operador “new” En x queda una referencia a un objeto de tipo ArrayList 2c2009 3 A3F Referencias Las variables son referencias a objetos: Date x, y; x = new Date (2009, 7, 25); y = x; “x” e “y” referencian al mismo objeto (hay una sola llamada a constructor) Si hago: y = new Date (1950, 8, 17); Ahora “y” referencia a otro objeto Una variable que no referencia un objeto tiene el valor “null” Puedo hacer: Date x = null; 2c2009 4 A3F Recolección de basura Si hago: Date x; x = new Date (2009, 7, 25); x = new Date (1950, 8, 17); El objeto inicial quedó como basura Java y C# 2c2009 5 A3F Paquetes Agrupación de clases, anidables Para manejar complejidad y resolver nombres Ejemplos: ArrayList es java.util.ArrayList (Java) ArrayList es System.Collections.ArrayList (C#) import java.util.*; // Java import java.util.ArrayList; // Java using System.Collections; // C# En Java no es necesario importar las clases de java.lang 2c2009 6 A3F Paquetes y fuentes Toda clase está en un paquete En Java existe el paquete “default”, pero no es recomendable usarlo En C#, se enmarca el código de la clase en una cláusula “namespace”: namespace carlosFontela.cuentas { … } En Java, cada clase pública va en un archivo fuente separado El paquete se indica en una cláusula “package” package carlosFontela.cuentas; 2c2009 7 A3F Implementaciones de mensajes en C# C# distingue “métodos” de “propiedades” Métodos: para comportamiento de los objetos lista.Add(-4); saludo2 = s.Replace (‘a’, ’u’); Propiedades: para consultar el estado Console.WriteLine (lista.Count); int longitud = saludo.Length; En C# hay propiedades de clase: DateTime ahora = DateTime.Now; “Now” es una propiedad de clase de la clase “DateTime” 2c2009 8 A3F Implementación de una clase (1) public class CuentaBancaria { // atributos: private int numero; private String titular; private double saldo; // propiedad (sólo lectura): public double getSaldo ( ) { return saldo; } 2c2009 9 A3F Implementación de una clase (2) // métodos: public void depositar (double monto) { if (monto < 0) throw new IllegalArgumentException ( ); saldo += monto; } public void extraer (double monto) { if (monto > saldo) throw new IllegalArgumentException ( ); saldo -= monto; } 2c2009 10 A3F Referencia this Objeto o referencia “this” public void depositar (double monto) { if (monto < 0) throw new IllegalArgumentException ( ); this.saldo = this.saldo + monto; } Invocación con el “objeto actual” c.depositar (2000); “this” referencia al “objeto actual” 2c2009 11 A3F Implementación de una clase (3) // constructor: public CuentaBancaria (int numero, String titular) { if (numero <= 0 || titular == null || titular == “”) throw new IllegalArgumentException ( ); this.numero = numero; this.titular = titular; this.saldo = 0; } } // fin de la clase 2c2009 12 A3F ¿Y en C#? Hay propiedades como elemento del lenguaje Uso: Console.WriteLine ( c.Saldo ); Definición: public double Saldo { get { return saldo; } } 2c2009 13 A3F Excepciones: captura Recordemos que las excepciones son objetos Se acceden mediante variables que los referencian Tienen estado, comportamiento e identidad Por ejemplo: try { ArrayList lista = new ArrayList (-5); } catch (IllegalArgumentException e) { e.printStackTrace(); } 2c2009 14 A3F Excepciones: lanzamiento Se crean y se lanzan hacia el módulo invocante Sintaxis: throw new ClaseException ( ); 2c2009 15 A3F Constructores Se usan para inicializar Si no se implementan hay uno por omisión Para que toda clase tenga el suyo Pero se pueden programar otros, como hicimos En este caso, deja de existir el default Debería dejar al objeto en un estado válido => debe cumplir con los invariantes El default no es seguro 2c2009 16 A3F Visibilidad (1) Atributos, propiedades y métodos privados Sólo se pueden usar desde dentro de la clase en que están definidos Atributos, propiedades y métodos públicos Se los puede usar desde cualquier lado Visibilidad de paquete (default, en Java) Se los puede usar desde su mismo paquete 2c2009 17 A3F Visibilidad (2) Atributos y métodos protegidos (protected) Son visibles sólo para las clases descendientes y las del propio paquete Poco uso; riesgos Clases con visibilidad “de paquete” Sólo se pueden usar dentro de su paquete ¿Clases privadas? Sólo cuando son clases internas No hay clases protegidas 2c2009 18 A3F Atributos de clase Supongamos que necesitamos que el número de cuenta fuera incremental Solución: Agregar un atributo “numeroAcumulado” que mantenga un único valor para la clase Eso es un atributo de clase En Java y C# “de clase” se dice “static” OJO: Los miembros de clase no se heredan 2c2009 19 A3F Java y .NET: clases utilitarias En Java y .Net toda función debe ir en una clase Aunque trabajemos sin objetos Usual agrupar funciones Clase utilitaria Agrupa métodos estáticos y/o constantes en una clase Esta clase no se puede instanciar Ejemplos Arrays.sort(v); double x = Math.pow(2,3); double p = Math.PI; 2c2009 20 A3F Java y .NET: sobrecarga public CuentaBancaria (int numero, String titular) { this ( numero, titular, 0); } public CuentaBancaria (int numero, String titular, int saldoInicial) { if (numero <= 0 || titular == null || titular == “”) throw new IllegalArgumentException ( ); this.numero = numero; this.titular = titular; this.saldo = saldoInicial; } 2c2009 21 A3F Java: no todo es un objeto Las instancias de tipos primitivos no son objetos int, long, double, char, boolean Y se manejan por valor (no por referencia) Hay “encajonamiento” o “autoboxing” Integer x = 4; // significa Integer x = new Integer(4); Arreglos “primitivos” no son objetos int [ ] v = new int [4]; Pero se manejan por referencia Tamaño se establece en tiempo de ejecución Una vez establecido, no puede variar Hay chequeo de rangos Tipo de los elementos definido estáticamente 2c2009 22 A3F C#: casi todo es un objeto Los tipos primitivos son tipos por valor Pero hay encajonamiento Los arreglos son instancias de System.Array Hay muchos tipos por valor Pero se manejan como clases Y se pueden definir por el programador Con algunas restricciones 2c2009 23 A3F Delegación Un objeto contiene referencias a otros objetos y les delega comportamiento public class Segmento { private Punto p1; private Punto p2; public double longitud ( ) { return p1.distancia(p2); } ... } 2c2009 24 A3F Herencia y lenguajes En Java, para indicar herencia public class Elipse extends Figura { ... } En C# reemplazamos “extends” por “:” public class Elipse : Figura { ... } 2c2009 25 A3F Herencia, variables y objetos “Casteo” hacia arriba automático Elipse e = new Elipse(); Figura f = e; // e = f; // válido y seguro inválido e inseguro Otro caso public void p (Figura x) { … } // … luego será invocado: p(e);// e es de tipo Elipse Los llamamos “objetos polimorfos” Parecen una cosa pero son otra f es una Elipse aunque se la trate como Figura Figura f = new Elipse ( ); 2c2009 // tipos distintos en la variable y el objeto !! 26 A3F Herencia y arreglos O arreglos polimorfos public Figura[ ] v = new Figura [3]; // luego… v[0] = new Elipse ( ); v[1] = new Circulo ( ); v[2] = new TrianguloRectangulo ( ); Pero Estamos perdiendo información del objeto Cada v[i] sólo va a tener disponibles los atributos y métodos públicos de Figura Ya volveremos sobre esto 2c2009 27 A3F Particularidades de Java y C# Jerarquía de raíz única Clase Object Todo se puede transformar en un Object Atributos y métodos comunes a todas las clases Otras importantes consecuencias Posibilidad de evitar la herencia Declaramos la clase como “final” Ejemplo: String public final class String {...} “sealed” en C# 2c2009 28 A3F Constructores y herencia Los constructores no se heredan Cada clase debe tener el suyo Receta Llamar al constructor del ancestro al principio del constructor propio // Java: public Derivada( ) { super(); ... } // C#: public Derivada( ) : base ( ) { ... } Automático con constructores por defecto 2c2009 29 A3F Redefinición en Java y C# Los métodos privados no pueden ser redefinidos Se estaría definiendo uno nuevo Posibilidad de evitar la redefinición Métodos “final” public final void m() {...} “sealed” en C# No se puede redefinir un método haciéndolo más privado Sobrecarga y redefinición Redefinición se hace con la misma firma Si no, es sobrecarga 2c2009 30 A3F Clases abstractas No tienen instancias Caso de CuentaBancaria si implemento CajaAhorro Pero pueden tener constructor ¡que no debe ser llamado nunca! Generalizan estructura y comportamiento de varias clases Caso del método depositar O crean una familia Se declara “abstract” 2c2009 31 A3F Analizar CuentaBancaria cb = new CuentaBancaria(…); CuentaCorriente cc1 = new CuentaCorriente(…); CuentaBancaria cc2 = new CuentaCorriente(…); cb.extraer(200); cc1.extraer(200); cc2.extraer(200); 2c2009 32 A3F Métodos virtuales (1) 2c2009 33 A3F Métodos virtuales (2) // llama al dibujar de Elipse: unaElipse.mover(); // llama al dibujar de Triangulo: unTriangulo.mover(); // llama al dibujar de la clase de cada figura: v.dibujarTodas(); // porque dibujar es virtual En Java y en Smalltalk la “virtualidad” se da por defecto En C# y C++ debemos declarar métodos como “virtual” Métodos de clase, privados y finales no son virtuales 2c2009 34 A3F Métodos virtuales (3) Los métodos virtuales agregan ineficiencias Pero garantizan reutilización Eliminar la “virtualidad” sólo si se demuestra que no se van a redefinir y la presunta ineficiencia Un método debe ser virtual sí o sí cuando se lo redefinirá y es llamado desde: Un método en una clase ancestro Un método que delegue en el método en cuestión de la clase ancestro 2c2009 35 A3F Ejemplo estándar en C# En Object public virtual String ToString ( ) { … } Console.WriteLine usa ToString() Si quiero que un objeto sea imprimible, debo redefinir ToString: En CuentaBancaria public String ToString() { return ( numero + titular + saldo.ToString() ); } Luego... CuentaBancaria cuenta = new CtaCte(12,”Juan”); Console.WriteLine(cuenta); 2c2009 36 A3F Métodos abstractos (1) Nunca van a ser invocados Caso del dibujar de Figura Además, definen que toda instancia de una subclase va a poder responder a ese mensaje Aunque sin definir comportamiento, porque a nivel de la clase madre no se puede prever En lenguajes compilados, es también una necesidad de compilación 2c2009 37 A3F Métodos abstractos (2) Corolarios No tienen implementación Deben redefinirse Deben ser virtuales En Java y C#: public void abstract dibujar(); Si una clase tiene métodos abstractos debe ser abstracta 2c2009 38 A3F Clases internas Pueden utilizar métodos y atributos privados de su clase externa (sin relación de herencia) Pueden ser descendientes de cualquier clase visible de la clase externa En una clase descendiente de la externa se puede declarar una clase interna que descienda de la interna del ancestro Se usan poco 2c2009 39 A3F Clases dentro de métodos y anónimas (Java) public class Externa { //... public void m ( ) { class Interna extends Base { // ... } Interna i = new Interna ( ); // ... } public class Externa { //... public Base m( ) { // ... return new Base( ) { // declaración de la // clase anónima ... } } } } 2c2009 40 A3F Manejo de problemas (v1) public Fraccion (int numerador, int denominador) { if (denominador == 0) throw new ArgumentException( ); this.numerador = numerador; this.denominador = denominador; } public Fraccion Dividir (Fraccion y) { if (y.numerador == 0) throw new DivideByZeroException( ); int numerador = this.numerador * y.denominador; int denominador = this.denominador * y.numerador; return new Fraccion(numerador, denominador); } 2c2009 41 A3F Manejo de problemas (v2) public Fraccion (int numerador, int denominador) { if (denominador == 0) throw new FraccionInvalidaException ( ); this.numerador = numerador; this.denominador = denominador; } public Fraccion Dividir (Fraccion y) { if (y.numerador == 0) throw new FraccionInvalidaException ( ); int numerador = this.numerador * y.denominador; int denominador = this.denominador * y.numerador; return new Fraccion(numerador, denominador); } 2c2009 42 A3F Clases de excepciones ¿Qué es FraccionInvalidaException? Una clase de excepción Debe derivar de Exception o una derivada public class FraccionInvalidaException : System.ApplicationException { } Lo que se lanza es un objeto, instancia de esa clase Las jerarquías influyen en la captura: Se captura cualquier clase derivada Ojo con el orden de captura 2c2009 43 A3F Jerarquías de excepciones (Java) En .NET todas las excepciones son “no chequeadas” 2c2009 44 A3F Excepciones chequeadas (Java, 1) Cláusula “throws” obligatoria public Fraccion dividir (Fraccion y) throws FraccionInvalidaException { if (y.numerador == 0) throw new FraccionInvalidaException ( ); int numerador = this.numerador * y.denominador; int denominador = this.denominador * y.numerador; return new Fraccion(numerador, denominador); } A lo sumo se puede declarar un ancestro En redefiniciones, mantener y no agregar Para mantener el polimorfismo: muy molesto 2c2009 45 A3F Excepciones chequeadas (Java, 2) Obligación de capturar (I) public Fraccion divisionMultiple ( Fraccion [ ] x, Fraccion [ ] y ) { Fraccion suma = new Fraccion (0, 1); try { for (int i = 0; i < 10; i++) { Fraccion d = x[i].dividir ( y [i] ); suma = suma.sumar(d); } } catch (FraccionInvalidaException e) { System.err.println(“División por cero”); return new Fraccion (0, 1); } return s; } 2c2009 46 A3F Excepciones chequeadas (Java, 3) Obligación de capturar (II) public Fraccion divisionMultiple ( Fraccion [ ] x, Fraccion [ ] y ) throws FraccionInvalidaException { Fraccion suma = new Fraccion (0, 1); for (int i = 0; i < 10; i++) { Fraccion d = x[i].dividir( y[i] ); suma = suma.Sumar(d); } return s; } 2c2009 47 A3F Excepciones chequeadas (Java, 4) Obligación de capturar (III) public Fraccion divisionMultiple ( Fraccion [ ] x, Fraccion [ ] y) { Fraccion suma = new Fraccion (0, 1); try { for (int i = 0; i < 10; i++) { Fraccion d = x[i].dividir( y[i] ); suma = suma.sumar(d); } } catch (FraccionInvalidaException e) { } return s; } 2c2009 48 A3F Lenguajes: enfoques Excepciones chequeadas Son más seguras Molesta tener que capturarlas sí o sí Limita la redefinición, al no poder agregar nuevas excepciones Aunque cumple el principio de substitución Microsoft diseñó .NET sin excepciones chequeadas C++ tiene un enfoque mixto Ojo: Java permite ambas Aunque es una decisión de diseño 2c2009 49 A3F Claves Java y C# son lenguajes de tipos estáticos La sintaxis deriva de C, pero la semántica no tanto C# maneja “propiedades”, Java no Más niveles de visibilidad que en Smalltalk Herencia implica posibilidades de “casteo hacia arriba” Polimorfismo por defecto en Java, no en C# Excepciones chequeadas en Java, no en C# Mejor soporte para clases y métodos abstractos que en Smalltalk 2c2009 50 A3F Qué sigue Interfaces, colecciones, genericidad Primer parcial Desarrollo de software y metodología 2c2009 51 A3F