0% encontró este documento útil (0 votos)
21 vistas40 páginas

Java: Uso de Enum, Interfaces y Generics

Cargado por

eia
Derechos de autor
© © All Rights Reserved
Nos tomamos en serio los derechos de los contenidos. Si sospechas que se trata de tu contenido, reclámalo aquí.
Formatos disponibles
Descarga como PPTX, PDF, TXT o lee en línea desde Scribd
0% encontró este documento útil (0 votos)
21 vistas40 páginas

Java: Uso de Enum, Interfaces y Generics

Cargado por

eia
Derechos de autor
© © All Rights Reserved
Nos tomamos en serio los derechos de los contenidos. Si sospechas que se trata de tu contenido, reclámalo aquí.
Formatos disponibles
Descarga como PPTX, PDF, TXT o lee en línea desde Scribd

Java

Enum
Interfaces
Generics
Enum
En su forma más simple se usan como los enum de C: como "contenedores de constantes"

public static final int SUCCESS = 0;


public static final int GOOD = 1;
public static final int REGULAR = 2;
public static final int BAD = 3;

public enum Rating {


SUCCESS,
SUCCESS, GOOD, REGULAR y BAD son
GOOD, las únicas instancias de Rating
REGULAR,
BAD
}
Enum
Como cualquier clase, puede tener métodos de clase.

public enum Rating {

SUCCESS, GOOD, REGULAR, BAD;

public static String toString(Rating rating) {


if (rating == null)
return "";
switch (rating) {
case SUCCESS: return "Success";
case GOOD: return "Good";
case REGULAR: return "Regular";
case BAD: return "Bad";
}
return "";
}
Enum
Como cualquier clase, puede tener métodos de clase

public static Integer intValue(Rating rating) {


if (rating == null)
return null;
switch (rating) {
case SUCCESS: return 10;
case GOOD: return 7;
case REGULAR: return 5;
case BAD: return 2;
default: return 0;
}
}

}
Enum
Como cualquier clase, puede tener métodos de instancia

public enum Rating {

SUCCESS, GOOD, REGULAR, BAD;

@Override
public String toString() {
switch ( this) {
case SUCCESS: return "Success";
case GOOD: return "Good";
case REGULAR: return "Regular";
case BAD: return "Bad"; O podemos tratar de
} hacerlo un poco mejor
return "";
}
Enum
Cada uno de los "valores" del enum son las posibles instancia de la clase, y por lo tanto puede tener
variables de instancia

public enum Rating {


@Override
SUCCESS("Success", 10), public String toString() {
GOOD("Good", 7), return description;
REGULAR("Regular", 5), }
BAD("Bad", 2);
public Integer intValue() {
private final String description; return value;
private final int value; }

Rating (String description, int value) { }


this.description = description;
this.value = value;
}
Ejercicio
Escribir una clase o método que permita realizar una operación matemática binaria representada
por tres Strings, los dos primeros los operandos y el tercero el operador.

"12.5" "34.0" "+" -> 46.5


"12.5" "4.0" "-" -> 8.5
"4" "12.0" "-" -> -8.0
"0.5" "2" "*" -> 1.0
"a" "0.5" "+" -> Alguna excepción

Para obtener el valor usamos Double.parseDouble(string)


Ejercicio
Opción 1: creamos una clase con un método que reciba los tres strings, y en base al operador
retorne el resultado

public class Evaluator {


public Double evaluate(String op1, String op2, String op) {
Háganlo en C, si
double n1 = Double.parseDouble(op1); tanto les gusta
double n2 = Double.parseDouble(op2);

switch (op) {
case "+": return n1 + n2;
case "-": return n1 - n2;
case "*": return n1 * n2;
case "/": return n1 / n2;
}
return null;
}
}
Ejercicio
Opción 2: creamos una clase para cada operador

public abstract class Operator {


public abstract double apply(double operand1, double operand2);
}

public class Sum extends Operator {


@Override
public double apply(double operand1, double operand2) {
return operand1 + operand2;
}
@Override
public String toString() {
return "+";
}
}
Ejercicio
Opción 2: creamos distintas clases para cada operador
public class Evaluator {
public static Double evaluate(String op1, String op2, String op) {

double n1 = Double.parseDouble(op1); En serio,


double n2 = Double.parseDouble(op2); vuelvan a C

switch (op) {
case "+": return new Sum().apply(n1, n2);
case "-": return new Sub().apply(n1, n2);
case "*": return new Mult().apply(n1, n2);
case "/": return new Div().apply(n1, n2);
}
return null;
}
}
Ejercicio
Opción 2 mejorada: usamos el patrón Singleton, y un método que determine la clase a usar
public abstract class Operator {
public abstract double apply(double operand1, double operand2);
}

public class Sum extends Operator {


private static final Sum instance = new Sum();

public static Sum getInstance() {


return instance;
}

private Sum() { }

@Override
public double apply(double operand1, double operand2) {
return operand1 + operand2;
}
@Override
public String toString() {
return "+";
}
}
Ejercicio
Opción 2 mejorada: usamos el patrón Singleton, y un método que determine la clase a usar

public class Evaluator {

public Double evaluate(String op1, String op2, String op) {

Operator oper = getOperator(op);


if (op == null)
return null;

return oper.apply(Double.valueOf(op1), Double.valueOf(op2));

...
Ejercicio
Opción 2 mejorada: usamos el patrón Singleton, y un método que determine la clase a usar

public Operator getOperator(String token) {


if (token.length() != 1) {
return null;
}
switch (token) {
case "+": return Sum.getInstance();
case "-": return Subtraction.getInstance();
case "*": return Multiplication.getInstance();
case "/": return Division.getInstance();
default: return null;
}
}
}
Ejercicio
Opción 3: en vez de Singleton, usar Enum

public enum Operation {


ADD {
public double apply(double a, double b) {
return a + b;
}
},
SUBTRACT {
public double apply(double a, double b) {
return a - b;
}
},
...

public abstract double apply(double a, double b);


}
Ejercicio
Opción 3: en vez de Singleton, usar Enum

public enum Operation {


ADD("+") {
public double apply(double a, double b) {
return a + b;
}
},
SUBTRACT("-") {
public double apply(double a, double b) {
return a - b;
}
},
... public abstract double apply(double a, double b);
private final String symbol;
@Override
Operation(String symbol) { public String toString() {return symbol; }
this.symbol = symbol;
} }
Enum: ordinal, valueOf y values

Operation addOp = Operation.ADD; 0


System.out.println(addOp.ordinal()); ADD
System.out.println(addOp);

7.5
Operation op = Operation.valueOf("ADD");
System.out.println(op.apply(4.5, 3));

8.5
for(Operation operation : Operation.values()) { -1.5
System.out.println(operation.apply(3.5, 5)); 17.5
} 0.7
Ejercicio
Una empresa de telefonía móvil cuenta con los siguientes planes de
datos mensuales:

● Prepago: el cliente tiene que comprar GB para poder navegar.


● Básico: $100 mensuales, incluye 1 GB de datos, $10 cada GB
extra
● Premium: $280 mensuales, incluye 3 GB, $8 cada GB extra
● Unlimited: $500 mensuales, sin límites de datos
Client someClient = new Client("John", DataPlan.PREMIUM);
Crear la clase Client que ofrece, al menos, métodos para consultar:
someClient.addGB(2);
● Si el consumo hasta el momento está dentro de los datos
System.out.println(someClient.belowLimit()); // true
someClient.addGB(2);
incluidos por el plan
System.out.println(someClient.belowLimit()); // false
● El monto de la factura según los consumos hasta el
System.out.println(someClient.total()); // momento
288.0
Interfaces

● Una interface es una referencia a un tipo en Java


● Es una colección de métodos abstractos y constantes
● Los métodos pueden tener un comportamiento por defecto
● Una clase puede implementar una o más interfaces.
● Un método puede especificar que retorne o recibe una interfaz. En tiempo de
ejecución retornará o recibirá una instancia de una clase que implemente dicha
interfaz
● Definición similar a una clase (package, .java, .class, etc.)
● Diferencias con una clase
○ No se pueden instanciar
○ No se puede especificar un constructor
○ No contiene variables de instancia
○ Una clase no extiende sino que implementa una interface
○ Una interfaz puede extender múltiples interfaces
Interfaces

Ejemplo: existen métodos (por ejemplo insertar ordenado en una colección, ordenar un vector,
etc.) que necesiten comparar dos elementos. Ese método puede exigir que las clases que le
pasen implementen una interfaz con un método de comparación.

public interface Comparable {


public int compareTo(Object other);
}

Esta interface no hace buen uso del lenguaje. Para ver mejores
ejemplos antes tenemos que explicar Generics.

public interface Comparable<T> {


int compareTo(T other);
}
Generics

Permiten la parametrización de tipos de datos (clases e interfaces)

Permiten escribir código reusable por objetos de distinto tipo

class Pair<T> { class Pair<K, V> {


private T first; private K key;
private T second; private V value;

public Pair(T first, T second) { public Pair(K key, V value) {


this.first = first; this.key = key;
this.second = second; this.value = value;
} }
... ...
} }
Generics

Ejemplo: un despachante de aduanas maneja operaciones marítimas, terrestres y aéreas.


Para cada una tiene tarifarios (con los costos) y en base a los tarifarios emiten cotizaciones a
sus clientes

public class TariffAir {...}


public class TariffOcean {...}

public class QuotationAir {


private TariffAir tariff;
...
public QuotationAir(TariffAir tariff, Client client, LocalDate dateFrom, ...)
{
...
}
} ¿No habrá métodos comunes ¿Y si necesito ver las
a las distintas opciones? cotizaciones de un cliente?
Generics

public abstract class Tariff {...}


public class TariffAir extends Tariff {...}
public class TariffOcean extends Tariff {...}

public abstract class Quotation {


private Tariff tariff;
...
public Quotation(Tariff tariff, Client client, LocalDate dateFrom, ...)
{
...
}

public Tariff getTariff() { ... }


} ¿Cómo verificar que una cotización
aérea referencia a un tarifario aéreo?
Generics
public abstract class Tariff {...}
public class TariffAir extends Tariff {...}
public class TariffOcean extends Tariff {...}

public abstract class Quotation<T extends Tariff> {


private T tariff;
...
public Quotation(T tariff, Client client, LocalDate dateFrom, ...) {
...
}
public T getTariff() { ... }
}

public class QuotationAir extends Quotation<Tariffair> {


public QuotationAir(TairffAir tariff, Client client, LocalDate dateFrom,) {
...
}
}
Arrays genéricos

Pair<String, String>[] v = new Pair[100];

v[0] = new Pair<>("100","Cien");

v[1] = new Pair(100.10, "Cien con 10");

Object[] v2 = v;

v2[2] = new Pair(100, "One hundred");

v = v2; Error: incompatible types: java.lang.Object[] cannot


be converted to Pair<String,String>[]
Generics: definición de una lista
public class LinkedList<E> {
public E get(int index) { ... }
public void add(E elem) { ... }
...
LinkedList<Integer> l = new LinkedList<>();
}

public class ArrayList<E> {


public E get(int index) { ... }
public void add(E elem) { ... }
... ¿Y si quiero hacer un método de clase
} que muestre todos o algunos
elementos de una lista cualquiera?
public class DoubleLinkedList<E> {
public E get(int index) { ... }
public void add(E elem) { ... }
...
}
Generics

public interface List<E> { Double sum(List<Double> aList){


boolean contains(E elem); ...
}
E get(int index);
void add(E elem);
void show(List aList) {
int size();
...
}
default E getFirst() {
return get(0); void show(List<?> aList) {
} ...
} }

List<Integer> l = new ArrayList<Integer>();

List<Integer> l = new ArrayList<>();


Generics: definición de una lista
public class LinkedList<E> implements List<E>{
public E get(int index) { ... }
public void add(E elem) { ... }
...
}

public class ArrayList<E> implements List<E> {


public E get(int index) { ... }
public void add(E elem) { ... }
...
}

public class DoubleLinkedList<E> implements List<E>{


public E get(int index) { ... }
public void add(E elem) { ... }
...
}
Comparator

Comparable exigía que la instancia sepa compararse contra otra instancia.


Comparator recibe dos instancias y las compara

@FunctionalInterface
public interface Comparator<T> {
int compare(T o1, T o2);

...
}
Ejemplo: una colección ordenada

Opción 1: La lista estará ordenada en base al "orden natural"

class SortedList<T extends Comparable<T>>


implements List<T> {

Los elementos a insertar deben


} implementar Comparable

class SortedList<T extends Comparable<? super T>>


implements List<T> {
Los elementos a insertar o una
} clase ancestro debe implementar
Comparable
Ejemplo: una colección ordenada

Opción 2: La lista estará ordenada por un criterio específico

class SortedList<T> implements List<T> {

private Comparator<? super T> cmp;

SortedList(Comparator<? super T> cmp) {


this.cmp = cmp;
}

}
Generics: métodos parametrizados
Se quiere pasar los elementos de un array a una lista

static void fromArrayToList(Object[] a, List<?> l) {


for (Object o : a) {
l.add(o); // error de compilación
}
}

static void fromArrayToList(Object[] a, List<Object> l) {


for (Object o : a) {
l.add(o);
}
Sólo se podrá usar con listas de Object, no Integer, Double,
}
etc.
static <T> void fromArrayToList(T[] a, List<T> l) {
for (T o : a) {
l.add(o);
}
}
Generics vs wildcards

interface Collection<E> {
boolean containsAll(Collection<?> c);
boolean addAll(Collection<? extends E> c);
}

interface Collection<E> {
<T> boolean containsAll(Collection<T> c);
<T extends E> boolean addAll(Collection<T> c);
}
Generics

Si el valor de retorno no depende del tipo parametrizado, se debe usar wildcard.

Usaremos métodos parametrizados si el tipo de valor de retorno es genérico o más de un


parámetro debe ser del mismo tipo.
Ejemplo: el despachante de aduana persiste todos los cambios hechos a una cotización.
Se necesita un método que retorne el estado a una revisión determinada.

public <Q extends Quotation> Q getVersion(Q quotation, int revision) {


Q old;
...
return old;
}
Interfaces
Un uso que se le puede dar a las interfaces es como "marcador": una interfaz que no
contiene métodos.

Las clases que las implementan no implementan comportamiento sino que son
"marcadas" para que otros métodos tomen alguna acción al respecto.

interface SecureResource {

void showPage(Page page) {


if ( page instanceof SecureResource ){
...
} NO hacer esto. Desde Java 1.5
existe una mejor forma de
} "marcar" o "anotar" una clase:
Annotations
Interfaces funcionales

Una interfaz con un único método abstracto puede implementarse tanto en forma
"tradicional" como en forma funcional.

package java.util.function;

@FunctionalInterface
public interface Function<T, R> {

R apply(T t);
...
}

void applyToArray(Double v[], Function<Double, Double> fn) {


for(Double x : v) {
x = fn.apply(x);
}
} No está cambiandolos
elementos del vector.
Interfaces funcionales

Una interfaz con un único método abstracto puede implementarse tanto en forma
"tradicional" como en forma funcional.

package java.util.function;
Para usar el método
@FunctionalInterface applyToArray podemos pasarle
public interface Function<T, R> { como segundo parámetro:
● Una instancia de una clase
R apply(T t);
... que implemente
} Function<Double,
Double>
void applyToArray(Double v[], Function<Double, Double> fn) {
for( int i= 0; i < v.length; i++) {
● Crear una clase anónima
v[i] = fn.apply(v[i]); ● Una expresión lambda
} (notación funcional)
}
Interfaces funcionales
class Sin implements Function<Double, Double> {
@Override
public Double apply(Double number) {
return Math.sin(number);
}
}

Double[] v = new Double[]{1.0, 2.0, 1.5, 0.5};

applyToArray(v, new Sin()); Clase Sin


applyToArray(v, new Function<Double, Double>() {
@Override
public Double apply(Double aDouble) { Clase
return Math.sin(aDouble); Anónima
}
});

applyToArray(v, x -> Math.sin(x)); Expresión Lambda


Function
Una interfaz funcional puede además contener constantes y métodos default y static.
@FunctionalInterface
public interface Function<T, R> {

R apply(T t);

default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {


Objects.requireNonNull(before);
return (V v) -> apply(before.apply(v));
}

default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {


Objects.requireNonNull(after);
return (T t) -> after.apply(apply(t));
}

static <T> Function<T, T> identity() {


return t -> t;
}

}
Ejemplo

Function<Integer, Integer> times2 = e -> e * 2;

Function<Integer, Integer> squared = e -> e * e;

times2.compose(squared).apply(4); // 32

times2.andThen(squared).apply(4); // 64
Cuadro resumen de tipos paramétricos

También podría gustarte