NOCIONES DE PROGRAMACIÓN Karim Guevara Puente de la Vega Agenda Conceptos de Programación Orientada al Objeto (OOP) Herencia Interfaces Clases Genéricas Programación Orientada a Objetos - POO Un objeto combina datos y operaciones (métodos) Encapsulamiento: ocultamiento de información Separa el «qué» (especificación funcional, pública) Del «cómo» (implementación, privada) Conceptos que apoyan la reutilización de código Código genérico: lógica del algoritmo independiente del tipo de datos. Herencia: extiende la funcionalidad de un objeto. Polimorfismo: selección automática de la operación apropiada según el tipo y número de los parámetros. Clases En Java un objeto es una instancia de una clase public class Entero{ private int valor; } public int leer(){ return valor; } public void guardar(int x){ = x; } valor public class Prueba{ public static void main(String[] args){ } } Entero m = new Entero(); m.guardar( 5 ); System.out.println(«m=« + m.leer()); Tipos de métodos Constructores – inicializan el objeto public class Fecha { private int a; private int m; private int d; public a = m = d = } public a = m = d = } } Fecha(int aa, int mm, int dd){ aa; mm; dd; Fecha(){ 2015; 4; 1; Fecha f1 = new Fecha(); Fecha f2 = new Fecha(2001,4,11); Tipos de métodos Mutators y Accessors – permiten acceder a las variables privadas para recuperar o modificar su valor. P.e. guardar(), leer() Aísla a los usuarios de una clase de los detalles de implementación de la clase, Evita que se vean afectados por cambios en la implementación toString Al imprimir un objeto a usando println, se invoca automáticamente a toString() Para imprimir objetos tipo Fecha, proveer una implementación de toString public String toString() { return d + "/" + m + "/" + a; } Tipos de métodos equals – se utiliza para evaluar si dos objetos tienen el mismo valor. if( x.equals(y) ) Declaración public boolean equals( Object b ) Object : tipo de objeto “universal” public boolean equals( Object b) { if ( !( b instanceof Fecha)) return false; Fecha f = (Fecha) b; return a==f.a && m==f.m && d==f.d; } this Identifica al objeto actual. Permite accede a los campos: this.a Compara si un objeto es el mismo que otro. Sirve como constructor, para llamar a otro constructor de la misma clase. public Fecha() { this( 2001, 1, 1 ); } Campos y métodos estáticos Hay dos tipos de campos estáticos public final static doble PI = 3.14159; //constante private static int precioActual = 1300; //variable compartida //para todos los objetos de la clase Los métodos estáticos están asociados a una clase, no a objetos particulares Math.sin Integer.parseInt No pueden hacer uso de la referencia this Packages Agrupan clases package P; class C { . . . } Si una variable no es public ni private, es visible solo dentro del mismo package La clase C se denomina P.C Si import P.C; o bien import P.* La clase se denota por C Herencia Permite reutilizar código ya desarrollado. Relación is-a P.e. Circulo is-a Figura Auto is-a Vehículo Las clases forman una jerarquía en base a la relación de herencia Otro tipo de relación es has-a: agregación P.e. Auto has-a Manubrio Herencia Clase base Clase derivada Posee otros atributos y/o métodos Redefine métodos Permite reutilizar código ya desarrollado. Los cambios efectuados en la clase derivada no afectan a la clase base public class Derivada extends Base { } . . . Constructores en la herencia Sino se define un constructor, se genera automáticamente un constructor sin parámetros: Llama al constructor con cero parámetros de la clase base, para la parte heredada Inicializa con valores nulos los campos restantes. Si hay un constructor, su primera instrucción puede ser una llamada a: super(…); Invoca al constructor de la clase base. final Si un método se declara como final, no puede ser redefinido en las clases derivadas. Una clase declarada como final no puede ser extendida Métodos abstractos Un método abstracto declara funcionalidad, pero no la implementa. Las clases derivadas deben proveer implementaciones para estos métodos. Una clase abstracta es una clase que tiene al menos un método abstracto. No se puede crear objetos pertenecientes a una clase abstracta. Métodos abstractos abstract class Figura { private String nombre; abstract public double area(); public Figura( String n ) { nombre = n; } // Este constructor no puede ser invocado directamente, // sólo lo usan las clases derivadas final public double compArea( Figura b ) { return area() - b.area(); } final public String toString() { return nombre + " de area " + area(); } } Métodos abstractos Clases derivadas public class Circulo extends Figura { static final private double PI = 3.141592653; private double radio; public Circulo( double r ) { super( "circulo" ); radio = r; } public double area() { return PI*radio*radio; } } Métodos abstractos Clases derivadas public class Circulo extends Figura { static final private double PI = 3.141592653; private double radio; public class Rectangulo extends Figura { double public Circulo( private double r ) { largo; super( "circulo" ); double ancho; private radio = r; } public Rectangulo( double l, double a ){ super( “rectangulo” ); public double area() { largo = l; return PI*radio*radio; ancho = a; } } } public double área() { return largo * ancho; } } Métodos abstractos Clases derivadas public class Circulo extends Figura { static final private double PI = 3.141592653; private double radio; public class Rectangulo extends Figura { double public Circulo( private double r ) { largo; super( "circulo" ); double ancho; private radio = r; } public Rectangulo( double l, double a ){ super( “rectangulo” ); public double area() { largo = l; return PI*radio*radio; ancho = a; } Cuadrado extends public class Rectangulo { } } public Cuadrado( double lado ){ super( lado, lado );public double área() { return largo * ancho; } } } } Interfaces Una interfaz es una clase en la que: Todos sus métodos son todos públicos y abstractos Todos sus atributos son públicos y finales Una interfaz no tiene, por lo tanto, constructor ni puede ser instanciada ¿Para qué se utiliza? Para permitir la herencia múltiple Definen un comportamiento (o funcionalidad) genérico, ignorando los aspectos relacionados con su implementación Interfaces P.e. se quiere definir el comportamiento de un vehículo terrestre: Todas las clases que implementen la interfaz VehiculoTerrestre deberán sobrescribir el método getNumRuedas public interface VehiculoTerrestre { int getNumRuedas(); } public class Coche implements VehiculoTerrestre{ private int numRuedas; public Coche( int numRuedas) { this.numRuedas = numRuedas; } public int getNumRuedas() { return numRuedas; } } La clase Coche implementa la interfaz VehiculoTerrestre La clase Coche debe implementar, por lo tanto, el método getNumRuedas Interfaces Se quiere definir el comportamiento de un vehículo acuático: public interface VehiculoAcuatico { double getNumRuedas(); } Al igual que hemos hecho antes, podríamos definir una clase Barco que implementará la interfaz VehiculoAcuatico ¿Y si queremos definir una clase Anfibio, que es tanto un vehículo terrestre como acuático? Interfaces public class Anfibio implements VehiculoAcuatico, VehiculoTerrestre { private int numRuedas; private double longEslora; public Anfibio(int numRuedas, double longEslora) { this.numRuedas = numRuedas; this.longEslora = longEslora; } public int getNumRuedas() { return numeroDeRuedas; } public double getLongEslora(){ return longEslora; } } Clases genéricas Java permite definir clases con variables o parámetros que representan tipos Esto es posible, cuando el tipo de un dato no afecta al tratamiento que se le va a dar a ese dato. Un mecanismo similar está disponible en lenguajes como C++ Template Antes se utilizaba la clase Object como “comodín” para simular los genéricos, pero esto conllevaba: Se podían mezclar objetos de las clases más dispares en una misma estructura contenedora de Object. Había que realizar muy a menudo castings Clases genéricas Clase ParEnteros public class ParEnteros { private int a, b; public ParEnteros( int a, int b){ this.a = a; this.b = b; } public ParEnteros swap (){ return new ParEnteros(b, a); } } Pero también necesitamos un clase ParCaracteres con los mismos métodos que la anterior Clases genéricas Clase ParEnteros y ParCaracteres public class ParEnteros { private int a, b; public ParEnteros( int a, int b){ this.a = a; public class ParCaracteres { this.b = b; private char a, b; } public ParCaracteres( char a, char b){ public ParEnteros swap (){ return new ParEnteros(b,this.a a); = a; this.b = b; } } } public ParEnteros swap (){ return new ParCaracteres(b, a); } } Clases genéricas y herencia Todas las clases en Java heredan de la clase Object. Se puede emplear Object para implementar algoritmos genéricos. P.e.: diseño de una clase Caja que pueda guardar objetos de cualquier tipo public class Caja { private Object dato; public Caja(){ super(); } public Object dame() { return dato; } public void pon( Object x ){ datos = x; } } public class TestCaja { public static void main( String args[]) { Caja c = new Caja(); c.pon( new Integer(34)); Integer n; n = (Integer) c.dame(); } } Clases genéricas y herencia Sin embargo, lo siguiente ¿daría error de compilación? ¿y en ejecución? public static void main( String args[]) { Caja c = new Caja(); c.pon( “Hola”); Integer n = (Integer) c.dame(); } } Generics aparece a partir del JDK 1.5. Ventajas: Detección de errores en tiempo de compilación Evitan las conversiones de tipo forzosas (castings) Disminuyen errores y mejoran la eficiencia Clases genéricas - Definición y uso Parámetro formal de tipo public class Caja <T> { private T dato; public Caja(){ super(); } public T dame() { return dato; } public void pon( T x ){ datos = x; } } El valor de la variable T puede ser cualquier clase o interfaz (nunca un tipo primitivo) Cuando se declara un objeto de tipo Caja es necesario indicar el tipo de la variable T. Caja <Integer> c; Clases genéricas - Definición y uso Ejemplo de uso de la clase genérica Caja: El siguiente programa ahora sí daría error de compilación: Clases genérica Par Clase ParEnteros y ParCaracteres public class ParEnteros { private int a, b; public ParEnteros( int a, int b){ this.a = a; public class ParCaracteres { this.b = b; private char a, b; } public ParCaracteres( char a, char b){ public ParEnteros swap (){ return new ParEnteros(b,this.a a); = a; this.b = b; } } } public ParEnteros swap (){ return new ParCaracteres(b, a); } } Clases genérica Par public class Par<T> { private T a, b; public Par(T a,Prueba T b){ { public class this.a static = a; public void main( String[] args ) { this.b = b; //Instanciación de la clase genérica para Integer // No se puede usar int porque no es una clase } Par<Integer> p = new Par<Integer>(1,2); } public Par<T> swap (){ // Instanciación de la clase genérica para Character return new Par<T>(b, a); } Par<Character> p1 = new Par<Character>('a','b'); p = p.swap(); p1 = p1.swap(); } } Clases genéricas estándar: clase ArrayList Hay multitud de clases genéricas en Java P.e. ArrayList, que implementa un array redimensionable Restricción de Tipos Genéricos – Clausula extends En algunas ocasiones es necesario indicar que el valor del tipo génerico no puede ser cualquiera P.e. Clase genérica Garaje public class Garaje <V> { <V extends Vehiculo> { private int numPlazas; private ArrayList<V> plazas; public Garaje( int numPlazas){ this.numPlazas = numPlazas; this.plazas = new ArrayList<V>(numPlazas); } ... } Si en el Garaje sólo pueden guardarse objetos Vehículo ? Métodos con Tipos Genéricos Es posible definir métodos que pueden manipular tipos genéricos, sin necesidad que estén contenidos en clases genéricas. P.e. la siguiente clase contiene un métodos genéricos que muestra el contenido del un ArrayList. public class Operaciones { . . . public static <T> void mostrarArray ( ArrayList<T> a) { for (int i=0; i < a.size(); i++) System.out.println(a.get(i).toString()); } } La Interfaz Comparable La interfaz genérica Comparable permite la comparación de dos objetos entre sí: public interface Comparable <T> { public int compareTo (T x); } Devuelve un número <0 si this < x >0 si this > x == 0 si this es igual a x Nota: se recomienda que a.compareTo(b) == 0 sea equivalente a a.equals(b) La Interfaz Comparable P.e. deseamos añadir a la clase Par un método max() public T max(){ return ((a>b) ? a : b); } Si añadimos este método sin más, obtendremos un error de compilación a y b no tienen por qué ser de un tipo comparable con >. La Interfaz Comparable Se debe indicar al compilador que el tipo T sólo representará tipos de valores comparables con el > o con una función equivalente (compareTo) public class Par <T extends Comparable> { private T a, b; public Par (T a, T b) { this.a = a; this.b = b; } public T max() { return ((a.compareTo(b) > 0) ? a : b); } . . . } La Interfaz Comparable P.e. deseamos tener un objeto Par de Rectangulos, y obtener el rectángulo mas grande (área): public class TestPar { public static void main (String[] args) { Rectangulo z = new Rectangulo(3,4); Rectangulo w = new Rectangulo(6,7); Par<Rectangulo> p = new Par<Rectangulo>(z,w); System.out.println(“Mayor = "+ p.max() ); } } La Interfaz Comparable class Rectangulo implements Comparable < Rectangulo > { private int base; Se requiere que la clase Rectangulo implemente la interfaz private int altura; Comparable y public el método compareTo: Rectangulo (int b, int a){ base = b; altura = a; } public int area(){ return base * altura;} public String toString(){ return "area:"+area(); } Compara las áreas de dos Rectangulos public int compareTo (Rectangulo r){ if (this.area() < r.area()) return -1; else if (this.area() > r.area()) return 1; else return 0; } }