This GitHub repo has example exercises for the ITBA Distributed Systems course. Develop practical skills and learn key concepts to master distributed systems.
View the Project on GitHub GonzaloHirsch/Programacion-de-Objetos-Distribuidos
Try with Resources es para evitar el null-check en el finally de un try-catch, los recursos se cierran solos en el finally, si es necesario:
public final String readFirstLineFromFile(String path) throws IOException
{
try (BufferedReader br = new BufferedReader(new FileReader(path))){
return br.readLine();
}
}
Son interfaces que tienen 1 solo método abstracto para implementar, y se puede agregar @FunctionalInterface
para que el compilador verifique que cumple lo anterior.
:
@FunctionalInterface
public interface MyFunctionalInterface<T> {
boolean apply(T t);
}
Algunos ejemplos de interfaces funcionales son:
Predicate<T>
–> Boolean-valued property of an objectConsumer<T>
–> Action performed on an objectFunction<T, R>
–> Function transforming T into RSupplier<T>
–> provide instance of TUnaryOperator<T>
–> Function from T to TBinaryOperator<T>
–> Function from (T,T) to TEstas interfaces pueden tener métodos estáticos o default que sirven como utility methods.
Son métodos con cuerpo dentro de las interfaces para definir comportamiento predefinido.
Cuando una interfaz extiende a otra con un método default:
public interface LazyComparator<T> extends Comparator<T> {}
public interface AbstractComparator<T> extends Comparator<T> {
Comparator<T> reversed();
}
public interface OverrideComparator<T> extends Comparator<T> {
default Comparator<T> reversed() {return this; }
}
Que pasa cuando dos clases definen el mismo método default:
La referencia se hace con ::
, y puede ser:
Integer::sum
)mySet::contains
)Class::new
)Java puede deducir el tipo genérico a partir del principio de la declaración:
// Java 7
Map<String, Map<String, String>> mapOfMaps = new HashMap <String, Map<String, String>> ();
// Java 8+
Map<String, Map<String, String>> mapOfMaps = new HashMap<>();
Expresan una instancia de una interfaz funcional, permiten código más simple y legible, pasa de:
EventQueue.invokeLater(
new Runnable() {
@Override
public void run() {
System.out.println("Running");
}
}
);
a
EventQueue.invokeLater(() -> System.out.println("Running"));
La sintaxis es:
// Parametros
(String x, int y) -> {
// Cuerpo
System.out.println("recibi" + x);
return y + 1;
}
Aunque tiene algunos Syntax Sugars:
(x, y) -> x + y
x -> "hola"
() -> 42
(String s) -> { System.out.println(s);}
Las lambdas también pueden usar type inference y target typing, en donde se infieren (si se puede) los tipos a partir de en donde se asigna y como.
Lexical Scoping, como no son inner classes, tienen el scope de quién las contiene.
Effectively Final, son variables que no están declaradas como finales, pero como no se modifican, se las toma como final. Dentro de una lambda, si uso una variable de quién la contiene, debe ser final o effectively final, y las variables dentro de las lambda no puede hacen shadowing de las de quién la contiene.
Por la captura de tipo y otras variables, reduce los memory leaks.
El optional
es un contenedor que puede o no tener un valor. Además se puede preguntar si existe un valor, y también generar comportamiento para si existe o no.
Para generar un optional
:
Optional<String> opt1 = Optional.empty();
Optional<Integer> opt2 = Optional.ofNullable(values.get("key"));
Optional<Integer> opt3 = Optional.of(values.get("key")); // Potenicial null pointer
Para usar los condicionales:
// Long version
Optional<Soundcard> soundcard = ...
if(soundcard.isPresent()){
System.out.println(soundcard.get());
}
// Method reference version
soundcard.ifPresent(System.out::println);
// Si no hay elemento
soundcard.orElse(otherSoundcard);
soundcard.orElseGet(() -> createSoundCard())
soundcard.orElseThrow(IllegalStateException::new)
Se pueden convertir las colecciones en streams, y ahi se pueden usar lambdas para transformar a la información. Los métodos son:
default Stream<E> stream()
default Stream<E> parallelStream()
default Spliterator<E> spliterator()
La sintaxis para usar streams es:
stream()
.Hay varias formas de construir un Stream
, algunas son:
stream()
Stream
como ::of(T…)
, ::empty()
, ::concat(Stream, Stream)
, ::generate(Supplier)
o ::iterate(seed, UnaryOperator)
Stream.Builder
Las operaciones intermedias generan nuevos streams, y son lazy, lo que significa que no se calculan hasta que una operación terminal las requiere. Algunas operaciones son:
equals()
Las operaciones terminales generan una nueva colección o un valor, y al aplicarse se cierra el Stream automáticamente.
El reduce aplica la función accumulator
entre lo acumulado y el próximo valor del stream, el identity
es el valor inicial. La última versión es solo para parallel streams, y aplica el combiner
entre resultados parciales:
reduce(BinaryOperator<T> accumulator)
reduce(T identity, BinaryOperator<T> accumulator)
reduce(U identity, BiFunction<U,? super T,U> accumulator, BinaryOperator<U>combiner)
El collect permite modificar/mutar los valores para obtener un valor final o colección de valores:
collect(Collector<? super T,A,R> collector)
collect(Supplier<R> supplier, BiConsumer<R,? super T> accumulator, BiConsumer<R,R> combiner)
Los collectors reciben los siguiente, aunque hay algunos ya definidos:
Supplier<A> supplier
–> para obtener la clase que acumularáBiConsumer<A,T> accumulator
–> función de la clase acumulador para “acumular”BinaryOperator<A> combiner
–> cómo combinar resultado de acumulaciones paralelasFunction<A, R> finisher
–> una función para aplicar sobre el resultado final (típicamente Identidad)Characteristics... characteristics
–> valores que permiten optimizar las transformaciones.La nueva API de Java Time cumple con los siguientes principios:
Viene con factory methods:
//Current Date
LocalDate today = LocalDate.now();
//Creating LocalDate with values
LocalDate firstDay_2014 = LocalDate.of(2014, Month.JANUARY, 1);
LocalDate.ofEpochDay(365);
LocalDate.ofYearDay(2014, 100);
LocalDate localDate = LocalDate.from(Instant.now());
//parse
LocalDateTime dt = LocalDateTime.parse("27::Apr::2014 21::39::48", DateTimeFormatter.ofPattern("d::MMM::uuuu HH::mm::ss"));
Los objetos pueden ser tipos de momento (X es localización):
También pueden ser localización:
También existen period que son rangos con precisión al día, o duration que son rangos con precisón al nano segundo