Tutorial sobre la clase Optional a profundidad


La clase Optional se introdujo en Java 8 y se utiliza para representar si un valor esta presente o no, esto ayuda a crear apis limpias y a evitar posibles NullPointerExceptions.

A continuación algunos ejemplos sobre como utilizar Optional en una aplicación:

Ejemplo Básico

Supongamos que tenemos el siguiente código:

public Person findPersonByName(String name) {
	return dao.findPersonByName(name);
}

El código a simple vista parece no tener ningún problema, ahora veamos el código que lo utiliza:

public void validate(String name) {
	Person result = findPersonByName(name);
	boolean adult = result.getAge() > 18;
	/**
	 * Other calculations 
	 */
}

Como vemos el método validate utiliza a findPersonByName para con base en el nombre determinar si es o no un adulto, el código no tendría ninguna falla siempre y cuando el nombre que se pasa como parámetro existe en la base de datos, en caso contrario se produciría una NullPointerException.

Veamos como solucionarlo sin utilizar la clase Optional:

public void validate(String name) {
	Person result = findPersonByName(name);
	if(result!=null){
		boolean adult = result.getAge() > 18;
	}else{
		/**
		 * take different actions
		 */
	}
}

La solución es funcional pero no es muy práctica, ya que depende mucho de quien consume el método realizar la validación o no.

Veamos la solución utilizando la clase Optional:

public Optional<Person> findPersonByName(String name) {
	return Optional.ofNullable(dao.findPersonByName(name));
}

public void validate(String name) {
	Optional<Person> result = findPersonByName(name);
	if (result.isPresent()) {
		boolean adult = result.get().getAge() > 18;
	} else {
		/**
		 * take different actions
		 */
	}
}

Como vemos el método findPersonByName() busca a la persona por nombre y utiliza Optional.ofNullable() para indicar que es posible que se tenga un valor nulo. Al devolver un objeto de tipo Optional en lugar de uno de tipo Person se da a conocer a los consumidores del api que el valor de retorno del método puede ser nulo así que deben utilizar el método isPresent para validar si existe el valor o no. Esto lo hace una solución muy elegante a demás que se evita generar NullPointerExceptions para casos cuando es posible tener un elemento nulo.

Utilizando valores por defecto 

Existen casos cuando en caso de no existir un valor tal vez quieres utilizar un valor por defecto en lugar de devolver la referencia null, veamos el siguiente ejemplo:

public Properties getApplicationProperties() {
	Properties props = new Properties();
	props.setProperty("applicationName", "Great application");
	return props;
}

public void configureCluster(){
	Properties properties = getApplicationProperties();
	String context= Optional.ofNullable(properties.getProperty("contextPath")).orElse("/api");
	System.out.println(context);
}

public static void main(String[] args) {
	new Configuration().configureCluster();
}

Si ejecutamos el código anterior la salida será /api porque obtenemos la propiedad contextPath, al ser nula devolvemos el valor /api utilizando el método orElse().

En el ejemplo anterior vimos como utilizar el método orElse(), la limitante que tiene es que recibe un objeto del mismo tipo que el objeto opcional así que solo podremos poner un valor en el sin lógica, existe una version similar llamada orElseGet() la cual en lugar de tomar el valor a devolver en caso de que el valor no se encuentre presente tomará una interfaz funcional la cual solo se invocará en caso de que el elemento no se encuentre presente.

Veamos un ejemplo

public void configureCluster() {
	Properties properties = getApplicationProperties();
	String context = Optional.ofNullable(properties.getProperty("contextPath")).orElseGet(() -> 
	getContextPathFromRemoteService()
	);
	System.out.println(context);
}

public String getContextPathFromRemoteService() {
	return Math.random() + "";
}

Como vemos, en caso de que la propiedad ‘contextPath’ no se encuentre en nuestro objeto properties se mandará a llamar el método getContextPathFromRemoteService(). 

Lanzando excepciones

En el ejemplo anterior vimos como devolver un valor por defecto en caso de que algo no esté presente, ahora veremos como mandar una excepción, veamos el siguiente ejemplo:

public Properties getApplicationProperties() {
	Properties props = new Properties();
	props.setProperty("applicationName", "Great application");
	return props;
}

public void configureCluster() {
	Properties properties = getApplicationProperties();
	String context = Optional.ofNullable(properties.getProperty("contextPath")).orElseThrow(IllegalStateException::new);
	/**
	 * configuring the application
	 */
}

Como se puede ver modificamos el ejemplo anterior para que en caso de que no se encuentre la propiedad contextPath se genere una excepción IllegalStateException y detenga el inicio de la aplicación.

En el siguiente post veremos como utilizar la clase Optional en conjunto con Streams.

Para estar al pendiente sobre nuestro contenido nuevo síguenos en nuestras redes sociales  https://www.facebook.com/devs4j/ y https://twitter.com/devs4j.

Autor: Alejandro Agapito Bautista

Twitter: @raidentrance

Contacto:raidentrance@gmail.com

Java 8 manejo de fechas y tiempo : ZonedDateTime, Period y Duration


En el post anterior Java 8 manejo de fechas y tiempo : LocalDate, LocalTime y LocalDateTime aprendimos como utilizar algunas clases para trabajar con fechas y tiempo, pero estas clases no consideran las zonas horarias, en este post analizaremos las clases ZonedDateTime, Period y Duration.

ZonedDateTime

ZonedDateTime se utiliza cuando queremos trabajar con fechas y tiempo pero agrega el factor de las zonas horarias, para esto utiliza un ZoneId el cual es un identificador para diferentes zonas, el siguiente código obtiene todos los ZoneId disponibles:

import java.time.ZoneId;
import java.util.Set;

/**
 * @author raidentrance
 *
 */
public class LocalDateExample {
	public static void main(String[] args) {
		Set availableZoneIds = ZoneId.getAvailableZoneIds();
		for (String zoneId : availableZoneIds) {
			System.out.println(zoneId);
		}
	}
}

Salida:

Asia/Aden
America/Cuiaba
Etc/GMT+9
Etc/GMT+8
Africa/Nairobi
America/Marigot
Asia/Aqtau
Pacific/Kwajalein
America/El_Salvador
Asia/Pontianak
Africa/Cairo
Pacific/Pago_Pago
Africa/Mbabane
Asia/Kuching
Pacific/Honolulu
Pacific/Rarotonga
America/Guatemala
Australia/Hobart
Europe/London
America/Belize
America/Panama
Asia/Chungking
America/Managua
America/Indiana/Petersburg
Asia/Yerevan
Europe/Brussels
GMT
Europe/Warsaw
America/Chicago
Asia/Kashgar
Chile/Continental
Pacific/Yap
CET
Etc/GMT-1
Etc/GMT-0
Europe/Jersey
America/Tegucigalpa
Etc/GMT-5
Europe/Istanbul
America/Eirunepe
Etc/GMT-4
America/Miquelon
Etc/GMT-3
Europe/Luxembourg
Etc/GMT-2
Etc/GMT-9
America/Argentina/Catamarca
Etc/GMT-8
Etc/GMT-7
Etc/GMT-6
Europe/Zaporozhye
Canada/Yukon
Canada/Atlantic
Atlantic/St_Helena
Australia/Tasmania
Libya
Europe/Guernsey
America/Grand_Turk
US/Pacific-New
Asia/Samarkand
America/Argentina/Cordoba
Asia/Phnom_Penh
Africa/Kigali
Asia/Almaty
US/Alaska
Asia/Dubai
Europe/Isle_of_Man
America/Araguaina
Cuba
Asia/Novosibirsk
America/Argentina/Salta
Etc/GMT+3
Africa/Tunis
Etc/GMT+2
Etc/GMT+1
Pacific/Fakaofo
Africa/Tripoli
Etc/GMT+0
Israel
Africa/Banjul
Etc/GMT+7
Indian/Comoro
Etc/GMT+6
Etc/GMT+5
Etc/GMT+4
Pacific/Port_Moresby
US/Arizona
Antarctica/Syowa
Indian/Reunion
Pacific/Palau
Europe/Kaliningrad
America/Montevideo
Africa/Windhoek
Asia/Karachi
Africa/Mogadishu
Australia/Perth
Brazil/East
Etc/GMT
Asia/Chita
Pacific/Easter
Antarctica/Davis
Antarctica/McMurdo
Asia/Macao
America/Manaus
Africa/Freetown
Europe/Bucharest
Asia/Tomsk
America/Argentina/Mendoza
Asia/Macau
Europe/Malta
Mexico/BajaSur
Pacific/Tahiti
Africa/Asmera
Europe/Busingen
America/Argentina/Rio_Gallegos
Africa/Malabo
Europe/Skopje
America/Catamarca
America/Godthab
Europe/Sarajevo
Australia/ACT
GB-Eire
Africa/Lagos
America/Cordoba
Europe/Rome
Asia/Dacca
Indian/Mauritius
Pacific/Samoa
America/Regina
America/Fort_Wayne
America/Dawson_Creek
Africa/Algiers
Europe/Mariehamn
America/St_Johns
America/St_Thomas
Europe/Zurich
America/Anguilla
Asia/Dili
America/Denver
Africa/Bamako
GB
Mexico/General
Pacific/Wallis
Europe/Gibraltar
Africa/Conakry
Africa/Lubumbashi
Asia/Istanbul
America/Havana
NZ-CHAT
Asia/Choibalsan
America/Porto_Acre
Asia/Omsk
Europe/Vaduz
US/Michigan
Asia/Dhaka
America/Barbados
Europe/Tiraspol
Atlantic/Cape_Verde
Asia/Yekaterinburg
America/Louisville
Pacific/Johnston
Pacific/Chatham
Europe/Ljubljana
America/Sao_Paulo
Asia/Jayapura
America/Curacao
Asia/Dushanbe
America/Guyana
America/Guayaquil
America/Martinique
Portugal
Europe/Berlin
Europe/Moscow
Europe/Chisinau
America/Puerto_Rico
America/Rankin_Inlet
Pacific/Ponape
Europe/Stockholm
Europe/Budapest
America/Argentina/Jujuy
Australia/Eucla
Asia/Shanghai
Universal
Europe/Zagreb
America/Port_of_Spain
Europe/Helsinki
Asia/Beirut
Asia/Tel_Aviv
Pacific/Bougainville
US/Central
Africa/Sao_Tome
Indian/Chagos
America/Cayenne
Asia/Yakutsk
Pacific/Galapagos
Australia/North
Europe/Paris
Africa/Ndjamena
Pacific/Fiji
America/Rainy_River
Indian/Maldives
Australia/Yancowinna
SystemV/AST4
Asia/Oral
America/Yellowknife
Pacific/Enderbury
America/Juneau
Australia/Victoria
America/Indiana/Vevay
Asia/Tashkent
Asia/Jakarta
Africa/Ceuta
Asia/Barnaul
America/Recife
America/Buenos_Aires
America/Noronha
America/Swift_Current
Australia/Adelaide
America/Metlakatla
Africa/Djibouti
America/Paramaribo
Europe/Simferopol
Europe/Sofia
Africa/Nouakchott
Europe/Prague
America/Indiana/Vincennes
Antarctica/Mawson
America/Kralendijk
Antarctica/Troll
Europe/Samara
Indian/Christmas
America/Antigua
Pacific/Gambier
America/Indianapolis
America/Inuvik
America/Iqaluit
Pacific/Funafuti
UTC
Antarctica/Macquarie
Canada/Pacific
America/Moncton
Africa/Gaborone
Pacific/Chuuk
Asia/Pyongyang
America/St_Vincent
Asia/Gaza
Etc/Universal
PST8PDT
Atlantic/Faeroe
Asia/Qyzylorda
Canada/Newfoundland
America/Kentucky/Louisville
America/Yakutat
Asia/Ho_Chi_Minh
Antarctica/Casey
Europe/Copenhagen
Africa/Asmara
Atlantic/Azores
Europe/Vienna
ROK
Pacific/Pitcairn
America/Mazatlan
Australia/Queensland
Pacific/Nauru
Europe/Tirane
Asia/Kolkata
SystemV/MST7
Australia/Canberra
MET
Australia/Broken_Hill
Europe/Riga
America/Dominica
Africa/Abidjan
America/Mendoza
America/Santarem
Kwajalein
America/Asuncion
Asia/Ulan_Bator
NZ
America/Boise
Australia/Currie
EST5EDT
Pacific/Guam
Pacific/Wake
Atlantic/Bermuda
America/Costa_Rica
America/Dawson
Asia/Chongqing
Eire
Europe/Amsterdam
America/Indiana/Knox
America/North_Dakota/Beulah
Africa/Accra
Atlantic/Faroe
Mexico/BajaNorte
America/Maceio
Etc/UCT
Pacific/Apia
GMT0
America/Atka
Pacific/Niue
Canada/East-Saskatchewan
Australia/Lord_Howe
Europe/Dublin
Pacific/Truk
MST7MDT
America/Monterrey
America/Nassau
America/Jamaica
Asia/Bishkek
America/Atikokan
Atlantic/Stanley
Australia/NSW
US/Hawaii
SystemV/CST6
Indian/Mahe
Asia/Aqtobe
America/Sitka
Asia/Vladivostok
Africa/Libreville
Africa/Maputo
Zulu
America/Kentucky/Monticello
Africa/El_Aaiun
Africa/Ouagadougou
America/Coral_Harbour
Pacific/Marquesas
Brazil/West
America/Aruba
America/North_Dakota/Center
America/Cayman
Asia/Ulaanbaatar
Asia/Baghdad
Europe/San_Marino
America/Indiana/Tell_City
America/Tijuana
Pacific/Saipan
SystemV/YST9
Africa/Douala
America/Chihuahua
America/Ojinaga
Asia/Hovd
America/Anchorage
Chile/EasterIsland
America/Halifax
Antarctica/Rothera
America/Indiana/Indianapolis
US/Mountain
Asia/Damascus
America/Argentina/San_Luis
America/Santiago
Asia/Baku
America/Argentina/Ushuaia
Atlantic/Reykjavik
Africa/Brazzaville
Africa/Porto-Novo
America/La_Paz
Antarctica/DumontDUrville
Asia/Taipei
Antarctica/South_Pole
Asia/Manila
Asia/Bangkok
Africa/Dar_es_Salaam
Poland
Atlantic/Madeira
Antarctica/Palmer
America/Thunder_Bay
Africa/Addis_Ababa
Europe/Uzhgorod
Brazil/DeNoronha
Asia/Ashkhabad
Etc/Zulu
America/Indiana/Marengo
America/Creston
America/Mexico_City
Antarctica/Vostok
Asia/Jerusalem
Europe/Andorra
US/Samoa
PRC
Asia/Vientiane
Pacific/Kiritimati
America/Matamoros
America/Blanc-Sablon
Asia/Riyadh
Iceland
Pacific/Pohnpei
Asia/Ujung_Pandang
Atlantic/South_Georgia
Europe/Lisbon
Asia/Harbin
Europe/Oslo
Asia/Novokuznetsk
CST6CDT
Atlantic/Canary
America/Knox_IN
Asia/Kuwait
SystemV/HST10
Pacific/Efate
Africa/Lome
America/Bogota
America/Menominee
America/Adak
Pacific/Norfolk
Europe/Kirov
America/Resolute
Pacific/Tarawa
Africa/Kampala
Asia/Krasnoyarsk
Greenwich
SystemV/EST5
America/Edmonton
Europe/Podgorica
Australia/South
Canada/Central
Africa/Bujumbura
America/Santo_Domingo
US/Eastern
Europe/Minsk
Pacific/Auckland
Africa/Casablanca
America/Glace_Bay
Canada/Eastern
Asia/Qatar
Europe/Kiev
Singapore
Asia/Magadan
SystemV/PST8
America/Port-au-Prince
Europe/Belfast
America/St_Barthelemy
Asia/Ashgabat
Africa/Luanda
America/Nipigon
Atlantic/Jan_Mayen
Brazil/Acre
Asia/Muscat
Asia/Bahrain
Europe/Vilnius
America/Fortaleza
Etc/GMT0
US/East-Indiana
America/Hermosillo
America/Cancun
Africa/Maseru
Pacific/Kosrae
Africa/Kinshasa
Asia/Kathmandu
Asia/Seoul
Australia/Sydney
America/Lima
Australia/LHI
America/St_Lucia
Europe/Madrid
America/Bahia_Banderas
America/Montserrat
Asia/Brunei
America/Santa_Isabel
Canada/Mountain
America/Cambridge_Bay
Asia/Colombo
Australia/West
Indian/Antananarivo
Australia/Brisbane
Indian/Mayotte
US/Indiana-Starke
Asia/Urumqi
US/Aleutian
Europe/Volgograd
America/Lower_Princes
America/Vancouver
Africa/Blantyre
America/Rio_Branco
America/Danmarkshavn
America/Detroit
America/Thule
Africa/Lusaka
Asia/Hong_Kong
Iran
America/Argentina/La_Rioja
Africa/Dakar
SystemV/CST6CDT
America/Tortola
America/Porto_Velho
Asia/Sakhalin
Etc/GMT+10
America/Scoresbysund
Asia/Kamchatka
Asia/Thimbu
Africa/Harare
Etc/GMT+12
Etc/GMT+11
Navajo
America/Nome
Europe/Tallinn
Turkey
Africa/Khartoum
Africa/Johannesburg
Africa/Bangui
Europe/Belgrade
Jamaica
Africa/Bissau
Asia/Tehran
WET
Europe/Astrakhan
Africa/Juba
America/Campo_Grande
America/Belem
Etc/Greenwich
Asia/Saigon
America/Ensenada
Pacific/Midway
America/Jujuy
Africa/Timbuktu
America/Bahia
America/Goose_Bay
America/Virgin
America/Pangnirtung
Asia/Katmandu
America/Phoenix
Africa/Niamey
America/Whitehorse
Pacific/Noumea
Asia/Tbilisi
America/Montreal
Asia/Makassar
America/Argentina/San_Juan
Hongkong
UCT
Asia/Nicosia
America/Indiana/Winamac
SystemV/MST7MDT
America/Argentina/ComodRivadavia
America/Boa_Vista
America/Grenada
Australia/Darwin
Asia/Khandyga
Asia/Kuala_Lumpur
Asia/Thimphu
Asia/Rangoon
Europe/Bratislava
Asia/Calcutta
America/Argentina/Tucuman
Asia/Kabul
Indian/Cocos
Japan
Pacific/Tongatapu
America/New_York
Etc/GMT-12
Etc/GMT-11
Etc/GMT-10
SystemV/YST9YDT
Europe/Ulyanovsk
Etc/GMT-14
Etc/GMT-13
W-SU
America/Merida
EET
America/Rosario
Canada/Saskatchewan
America/St_Kitts
Arctic/Longyearbyen
America/Fort_Nelson
America/Caracas
America/Guadeloupe
Asia/Hebron
Indian/Kerguelen
SystemV/PST8PDT
Africa/Monrovia
Asia/Ust-Nera
Egypt
Asia/Srednekolymsk
America/North_Dakota/New_Salem
Asia/Anadyr
Australia/Melbourne
Asia/Irkutsk
America/Shiprock
America/Winnipeg
Europe/Vatican
Asia/Amman
Etc/UTC
SystemV/AST4ADT
Asia/Tokyo
America/Toronto
Asia/Singapore
Australia/Lindeman
America/Los_Angeles
SystemV/EST5EDT
Pacific/Majuro
America/Argentina/Buenos_Aires
Europe/Nicosia
Pacific/Guadalcanal
Europe/Athens
US/Pacific
Europe/Monaco

La salida anterior representa todos los ZoneId disponibles, veamos como crear un objeto ZoneId utilizándolos:

ZoneId zoneIdMx = ZoneId.of("America/Mexico_City");
ZoneId zoneIdAm = ZoneId.of("Europe/Amsterdam");

Lo anterior crea dos objetos de la clase ZoneId uno con la zona de la Ciudad de México y otro con la de Amsterdam, ahora simularemos un vuelo que sale de la Ciudad de México rumbo a Amsterdam y veremos la hora de salida y la de aterrizaje en las dos zonas horarias.

ZoneId zoneIdMx = ZoneId.of("America/Mexico_City");
ZoneId zoneIdAm = ZoneId.of("Europe/Amsterdam");
ZonedDateTime departureDate = ZonedDateTime.of(2018, 10, 31, 2, 39, 0, 0, zoneIdMx);

System.out.println("Departure date: " + departureDate);
System.out.println("Arrival in México time: " + departureDate.plusHours(12));
System.out.println("Arrival in Amsterdam time: " + ZonedDateTime.ofInstant(departureDate.plusHours(12).toInstant(), zoneIdAm));

Salida:

Departure date: 2018-10-31T02:39-06:00[America/Mexico_City]
Arrival in México time: 2018-10-31T14:39-06:00[America/Mexico_City]
Arrival in Amsterdam time: 2018-10-31T21:39+01:00[Europe/Amsterdam]

El código anterior nos permite agregar el tiempo del vuelo y ver la hora en el destino y en el origen.

Period

La clase Period se utiliza para modificar valores de una fecha u obtener la diferencia entre dos fechas.

En el siguiente ejemplo tomaré mi cumpleaños 19 de agosto y calcularé periodo de tiempo que falta desde la fecha actual que es 31 de octubre del 2018:

LocalDate currentTime = LocalDate.now();

LocalDate myBirthDate = LocalDate.parse("2019-08-19");

Period period = Period.between(currentTime, myBirthDate);
System.out.println(
		String.format("Years %d Months %d Days %d", period.getYears(), period.getMonths(), period.getDays()));

Salida:

Years 0 Months 9 Days 19

Podemos ver que el periodo que falta son 0 años 9 meses y 19 días.

Duration

La clase Duration funciona de forma similar que Period la única diferencia es que en lugar de trabajar con fechas trabaja con tiempo, veamos el siguiente ejemplo:

Es la 1:35 pm y mi hora de salida del trabajo es a las 5:30 pm calculemos cuantos minutos faltan para salir:

LocalTime currentTime = LocalTime.of(1, 35);
LocalTime timeToLeave=LocalTime.of(5, 30);
Duration duration = Duration.between(currentTime, timeToLeave);
System.out.println(String.format("Minutes  %s", duration.toMinutes()));

Salida:

Minutes  235

Estos posts explican como facilitar el uso de fechas y horas lo cual se vuelve un dolor de cabeza para muchos desarrolladores. Para estar al pendiente sobre nuestro contenido nuevo síguenos en nuestras redes sociales  https://www.facebook.com/devs4j/ y https://twitter.com/devs4j.

Autor: Alejandro Agapito Bautista

Twitter: @raidentrance

Contacto:raidentrance@gmail.com

Java 8 manejo de fechas y tiempo : LocalDate, LocalTime y LocalDateTime


localdatesUno de los problemas más comunes a los que se enfrentan los desarrolladores es el manejo de fechas, tiempo y zonas horarias, para resolver este problema se solían utilizar api’s como Joda Time, Java 8 incluyó un conjunto de apis que nos ayudan a resolverlo sin incluir dependencias adicionales.

LocalDate, LocalTime y LocalDateTime

Las clases más comunes para el manejo de fechas con java 8 son LocalDate, LocalTime y LocalDateTime, se utilizan cuando la zona horaria no es requerida.

LocalDate

Un LocalDate representa una fecha en formato ISO (yyyy-MM-dd) sin tiempo. Veamos algunos ejemplos:

LocalDate date = LocalDate.now();
System.out.println(date);

El código anterior tendrá la siguiente salida(Entendiendo que el post se escribió el 30-10-2018):

2018-10-30

Como vemos es una fecha sin tiempo y sin zona horaria. Veamos algunas otras formas de crear un LocalDate:

LocalDate date2 = LocalDate.of(2018, 10, 30);
LocalDate date3 = LocalDate.parse("2018-10-30");

Las 2 expresiones crearán objetos de tipo LocalDate con la fecha del 30-10-2018.

Operaciones que se pueden realizar con LocalDate

Una vez que sabemos como construir un LocalDate el siguiente paso será conocer el tipo de operaciones que se pueden realizar con el.

  • Manipulación de fechas (Sumar o restar días, meses, años, etc ):
LocalDate date = LocalDate.parse("2018-10-30");
LocalDate newDate = date.plusDays(10);
System.out.println(date);
System.out.println(newDate);

Salida:

2018-10-30
2018-11-09

Como vemos a la fecha inicial se le sumaron 10 días y el mes se actualizó de forma automática, esto nos permite evitar considerar el número de días en un mes, el horario de verano, etc.

LocalDate date = LocalDate.parse("2018-10-30");
LocalDate newDate = date.plusMonths(3);
System.out.println(date);
System.out.println(newDate);

Salida:

2018-10-30
2019-01-30

De igual forma podemos hacerlo con los meses y LocalDate resolverá si es necesario cambiar de año.

Recordemos que cuando hacemos operaciones sobre las fechas debemos asignar la respuesta a una nueva referencia ya que el objeto original no se modificará puesto que los objetos LocalDate son immutables.

  • Comparación entre fechas

Así como podemos realizar operaciones entre las fechas podemos hacer comparaciones entre ellas, veamos algunos ejemplos:

Valida si una fecha es antes que otra:

System.out.println(LocalDate.parse("2018-10-30").isBefore(LocalDate.parse("2018-10-31")));

Salida

true

Valida si un año es bisiesto:

System.out.println(LocalDate.parse("2018-10-30").isLeapYear());

Salida:

false

Podemos realizar validaciones muy simples sin necesidad de escribir código complejo.

  • Obtener información sobre alguna fecha

El siguiente paso será obtener información sobre alguna fecha en específico veamos algunos ejemplos:

Obtener el día de la semana de mi cumpleaños:

System.out.println(LocalDate.parse("2019-08-19").getDayOfWeek());

Salida:

MONDAY

Extraer información de una fecha:

System.out.println(LocalDate.parse("2019-08-19").getDayOfWeek());

Salida:

19

Si lo único que necesitamos de una fecha es el mes podemos fácilmente extraerlo .

Podemos ver el detalle de todos los métodos disponibles de la clase LocalDate en el siguiente link.

LocalTime

LocalTime representa una hora sin la fecha, del mismo modo que con LocalDate podemos crearlo haciendo uso de los métodos now(), parse(..) y of(..), veamos algunos ejemplos:

LocalTime time = LocalTime.now();
LocalTime time2 = LocalTime.parse("11:00:59.759");
LocalTime time3 = LocalTime.of(11, 00, 59);
System.out.println(time);
System.out.println(time2);
System.out.println(time3);

Salida:

11:02:06.198
11:00:59.759
11:00:59

Las 3 anteriores son formas válidas de crear un objeto LocalTime. Veamos algunas de las operaciones que podemos realizar.

  • Modificar un LocalTime

La primera operación que veremos es como modificar un LocalTime:

LocalTime time = LocalTime.parse("11:00:59.759");
LocalTime time2 = time.plusHours(1);
System.out.println(time2);

Salida:

12:00:59.759

Con el método plusHours crearemos un nuevo LocalTime con la nueva hora calculada.

  • Validar un LocalTime

El siguiente punto será hacer validaciones sobre un LocalTime:

LocalTime time = LocalTime.parse("11:00:59.759");
LocalTime time2 = LocalTime.parse("12:00:59.759");
System.out.println(time.isBefore(time2));

Salida:

true

Usando métodos como isBefore podremos saber si una hora es mayor a otra.

  • Extraer información de una hora:

El siguiente paso será extraer solo una parte del objeto LocalTime:

LocalTime time = LocalTime.parse("11:00:59.759");
System.out.println(time.getHour());

Salida:

 11 

Esto lo utilizaremos en caso de que nuestra aplicación utilice solo la hora para realizar algún proceso.

LocalDateTime

La siguiente clase a analizar será LocalDateTime la cual representa una combinación entre LocalDate y LocalTime, veamos como crearlo:

LocalDateTime dateTime = LocalDateTime.now();
LocalDateTime dateTime1=LocalDateTime.of(2018, 10, 10, 11, 25);
LocalDateTime dateTime2=LocalDateTime.parse("2018-10-10T11:25");

System.out.println(dateTime);
System.out.println(dateTime1);
System.out.println(dateTime2);

Salida:

2018-10-30T11:46:58.274
2018-10-10T11:25
2018-10-10T11:25

En la salida anterior podemos ver como la salida incluye fecha y hora por cada objeto. Veamos algunas de las operaciones que podemos realizar con estos objetos.

  • Manipular un LocalDateTime:

De igual forma que con los anteriores podemos realzar manipulaciones sobre el LocalDateTime:

LocalDateTime dateTime=LocalDateTime.parse("2018-10-10T11:25");
LocalDateTime newDateTime = dateTime.plusDays(1).plusHours(2);

System.out.println(newDateTime);

Lo anterior creará un objeto LocalDateTime le agregará 1 día y después 2 horas, recordemos que debemos asignar el resultado a una nueva referencia ya que el objeto original no se modificará sino que se devolverá uno nuevo.

  • Realizar validaciones sobre un LocalDateTime

Como lo vimos en los ejemplos anteriores podemos realizar validaciones sobre el LocalDateTime:

LocalDateTime dateTime=LocalDateTime.parse("2018-10-10T11:25");
LocalDateTime dateTime2=LocalDateTime.parse("2019-10-10T11:25");

System.out.println(dateTime.isBefore(dateTime2));

Salida:

true

Se realizará del mismo modo que en los casos anteriores solo que ahora considerará tanto la fecha como la hora.

Esta es una guía rápida sobre como utilizar algunas clases del api de fechas y tiempo de java, en futuros posts hablaremos sobre como trabajar con zonas horarias, periodos e instantes. Para estar al pendiente sobre nuestro contenido nuevo síguenos en nuestras redes sociales  https://www.facebook.com/devs4j/ y https://twitter.com/devs4j.

Autor: Alejandro Agapito Bautista

Twitter: @raidentrance

Contacto:raidentrance@gmail.com

Java 8 Streams – Transformaciones utilizando stream().map


Continuado con los posts sobre Streams en este post explicaremos como transformar un objeto a otro utilizando la función map. Uno de los usos comunes de esto es transformar objetos a DTO’s así que tomaremos este caso como ejemplo.

Definiendo el modelo

Antes de comenzar a utilizar streams definiremos dos clases a utilizar Person y PersonDto, las cuales serán la clase base de mi aplicación y la clase a la que quiero transformar mi objeto.

Person.java

public class Person {
	private String firstName;
	private String middleName;
	private String lastName;
	private String address;

	public Person() {
	}

	public Person(String firstName, String middleName, String lastName, String address) {
		super();
		this.firstName = firstName;
		this.middleName = middleName;
		this.lastName = lastName;
		this.address = address;
	}

	public String getFirstName() {
		return firstName;
	}

	public void setFirstName(String firstName) {
		this.firstName = firstName;
	}

	public String getMiddleName() {
		return middleName;
	}

	public void setMiddleName(String middleName) {
		this.middleName = middleName;
	}

	public String getLastName() {
		return lastName;
	}

	public void setLastName(String lastName) {
		this.lastName = lastName;
	}

	public String getAddress() {
		return address;
	}

	public void setAddress(String address) {
		this.address = address;
	}

}

PersonDto.java

public class PersonDto {
	private String firstName;
	private String middleName;
	private String lastName;
	private String address;

	public PersonDto() {
	}

	public PersonDto(String firstName, String middleName, String lastName, String address) {
		super();
		this.firstName = firstName;
		this.middleName = middleName;
		this.lastName = lastName;
		this.address = address;
	}

	public String getFirstName() {
		return firstName;
	}

	public void setFirstName(String firstName) {
		this.firstName = firstName;
	}

	public String getMiddleName() {
		return middleName;
	}

	public void setMiddleName(String middleName) {
		this.middleName = middleName;
	}

	public String getLastName() {
		return lastName;
	}

	public void setLastName(String lastName) {
		this.lastName = lastName;
	}

	public String getAddress() {
		return address;
	}

	public void setAddress(String address) {
		this.address = address;
	}
}

Las dos clases anteriores servirán como base para realizar mis transformaciones.

Creando objetos de ejemplo

De el mismo modo que en el post anterior, en este post utilizaremos Java-Faker para generar datos de ejemplo más información de como utilizarlo en el siguiente enlace Java-Faker.

public static List getNames() {
	Faker faker = new Faker();
	List names = new ArrayList();
	for (int i = 0; i <= 1000; i++) {
		names.add(new Person(faker.name().firstName(), faker.name().firstName(), faker.name().lastName(),
				faker.address().fullAddress()));
	}
	return names;
}

El método anterior utiliza un Faker para generar datos de ejemplo y genera 1000 objetos Person con datos aleatorios.

Transformando objetos de la clase Person a PersonDto

Con el código anterior seremos capaces de generar 1000 objetos de tipo Person, el siguiente paso será transformarlos a objetos tipo PersonDto, veamos el siguiente ejemplo:

public static void main(String[] args) {
	List names = getNames();
	List peopleDtos = names.stream().map(person -> {
		return new PersonDto(person.getFirstName(), person.getMiddleName(), person.getLastName(),
				person.getAddress());
	}).collect(Collectors.toList());
	for (PersonDto personDto : peopleDtos) {
		System.out.println("First name: " + personDto.getFirstName());
		System.out.println("Middle name: " + personDto.getMiddleName());
		System.out.println("Last name: " + personDto.getLastName());
		System.out.println("Address: " + personDto.getAddress());
		System.out.println("--------");
	}
}

El código anterior realiza lo siguiente:

  1. Obtiene los 1000 objetos tipo Person a transformar
  2. Utilizando Streams se ejecutará lo siguiente:
    1. Crear un stream
    2. Transformar un stream de objetos de tipo Person a uno de tipo PersonDto basado en el lambda que define la transformación
    3. Transformar la respuesta a una lista
  3. Se imprimen los elementos pero ya con una lista tipo PersonDto

Salida:

--------
First name: Kenyatta
Middle name: Vallie
Last name: Runte
Address: Suite 750 049 Darrin Ford, East Minastad, NY 65986
--------
First name: Sedrick
Middle name: Cara
Last name: Willms
Address: 36767 Lueilwitz Pines, Delphineborough, NV 41658
--------
First name: Luisa
Middle name: Merritt
Last name: Weissnat
Address: 00304 Cummings Viaduct, North Elainaland, WI 84019-0601
--------
First name: Aniyah
Middle name: Deangelo
Last name: Keebler
Address: 37324 Prosacco Grove, North Shaniachester, MD 54667
--------
First name: Edwardo
Middle name: General
Last name: Fadel
Address: 54550 Grimes Forge, Trantowborough, VA 14184-7842
--------
First name: Jensen
Middle name: Edd
Last name: Rutherford
Address: 31157 McGlynn Isle, Moiseston, HI 99805
--------
1000 more...

Esto facilita la transformación de objetos de una clase a otra y es muy util al momento de escribir servicios web.

Para enterarte sobre futuros posts te recomendamos seguirnos en nuestras redes sociales: https://twitter.com/geeks_mx y https://www.facebook.com/geeksJavaMexico/.

Autor: Alejandro Agapito Bautista

Twitter: @raidentrance

Contacto:raidentrance@gmail.com

Java 8 Streams – Filtering


Siguiendo con la serie sobre Java Streams, en este post analizaremos como funcionan los filtros sobre Streams de Java a través de ejemplos.

Preparando el ejemplo

Antes de empezar con los ejemplos debemos entender como funciona un api que utilizaremos en nuestros ejemplos su nombre es Java Faker, puedes aprender más sobre la misma en el siguiente enlace java-faker. Este api nos permitirá generar datos de ejemplo.

Problema a resolver

Dado un arreglo de 1000 nombres buscar todos los que inician con la letra A, veamos el código que genera la lista de nombres:

public static List getNames() {
	Faker faker = new Faker();
	List names = new ArrayList();
	for (int i = 0; i <= 1000; i++) {
		names.add(faker.name().firstName());
	}
	return names;
}

Haciendo uso del API Java Faker generamos 1000 nombres aleatorios y los devolvemos.

Resolviendo el problema utilizando streams

Una vez que ya sabemos como obtener la lista de nombres el siguiente paso será implementar la lógica para filtrar solo los nombres que inician con la letra A, veamos el código:

public static void main(String[] args) {
	List names = getNames();
	List collect = names.stream()
			.filter(name -> name.startsWith("A"))
			.collect(Collectors.toList());
	for (String string : collect) {
		System.out.println(string);
	}
}

En el código anterior se puede ver que es muy simple hacer el filtro utilizando una función lambda que determina que registros se permitirán y cuales no, veamos la salida:

Alford
Annetta
Adonis
Alfred
Anais
Ashly
Aletha
Aniya
Alanna
Ahmed
Asa
Arlo
Angeline
Alexzander
Alexane
America
Annabell
Alexandra
Alysson
Amparo
Agustin
Anya
Aurore
Abe
Augustine
Adell
Ava
Alva
Aiyana
Augustus
Asha
Alvera
Abner
Ashlee
Anais
Andrew
Alejandra
Amely
Annalise
Adelia
Alf
Ashley
Aylin
Aubrey
Abelardo
Angelina
Andres
Agustina
Aileen
Alexys
Aglae
Albina
Anais
Alexander
Ashtyn
Adelbert
Alek
Alaina
Asia
Ari
Ara
Arturo
Aubree
Anne
Amelia
Alfreda
Aditya
Anais
Alex
Alexandre
Ara
Alize
Autumn
Anabel
Aletha
Akeem
Antonietta
Aliyah
Abdul
Arturo
Ashton
Abner
Alisa
Abbigail
Amber
Anais
Alanis
Abe
Amiya
Alvah
Alejandrin
Angelita

Solo nombres que contengan la letra A serán mostrados debido al filtro que realizamos.

Para enterarte sobre futuros posts te recomendamos seguirnos en nuestras redes sociales: https://twitter.com/geeks_mx y https://www.facebook.com/geeksJavaMexico/.

Autor: Alejandro Agapito Bautista

Twitter: @raidentrance

Contacto:raidentrance@gmail.com

 

Java 8 Streams – Remueve valores duplicados en una lista


En el ejemplo del día de hoy hablaremos de un problema común, remover datos duplicados de una lista, como sabemos existen muchos algoritmos para hacerlo, en este post hablaremos de dos formas utilizando Streams.

1 Utilizando distinct()

El método distinct nos permitirá remover datos duplicados de una lista.

public static void main(String[] args) {
	List numbers = Arrays.asList(7, 7, 7, 7, 2, 2, 2, 3, 3, 3, 3, 100, 100, 200, 200);
	numbers = numbers.stream().distinct().collect(Collectors.toList());
	System.out.println(numbers);
}

En el código anterior podemos ver que generamos una lista con muchos números duplicados, generamos un stream y hacemos uso de distinct() lo cual removerá datos duplicados y lo devolvemos como lista, con lo anterior tendremos la siguiente salida:

[7, 2, 3, 100, 200]

Como se puede ver, aunque en la lista original tenemos repetidos los números multiples veces, al utilizar distinct() todos los elementos duplicados fueron removidos y tendremos resultados únicos.

2 Utilizando set

Como sabemos existe una estructura de datos en Java llamada Set la cuál solo almacena datos únicos y es muy utilizada para remover datos duplicados, veamos un ejemplo:

public static void main(String[] args) {
	List numbers = Arrays.asList(7, 7, 7, 7, 2, 2, 2, 3, 3, 3, 3, 100, 100, 200, 200);
	Set nums = numbers.stream().collect(Collectors.toSet());
	System.out.println(nums);
}

En el código anterior podemos ver que nuestra entrada es una lista y lo único que hacemos es a través del método collect(..) la transformamos a un Set y con esto aseguramos que la salida será solo datos únicos, veamos la salida:

[2, 3, 100, 7, 200]

Del mismo modo que con el método distinct el resultado es un conjunto de números únicos en la salida, es importante considerar el tipo de colección que queremos utilizar en nuestro problema ya que eso determinará cuál de los dos mecanismos utilizaremos.

Para enterarte sobre futuros posts te recomendamos seguirnos en nuestras redes sociales: https://twitter.com/geeks_mx y https://www.facebook.com/geeksJavaMexico/.

Autor: Alejandro Agapito Bautista

Twitter: @raidentrance

Contacto:raidentrance@gmail.com

Java 8 Streams – Buscando el valor mínimo y máximo


Continuado con los posts sobre Streams en este post explicaremos como realizar búsquedas de el valor mínimo y máximo en una lista de números utilizando Streams.

Solución común

A continuación presentamos la solución común para encontrar el elemento mínimo de una lista:

public static void main(String[] args) {
	List numbers = Arrays.asList(7, 2, 3, 100, 200, 300, 400, 5, 1);
	int min = numbers.get(0);
	for (Integer value : numbers) {
		if (value < min) {
			min = value;
		}
	}
	System.out.println(min);
}

El código anterior devuelve el elemento más pequeño en la lista.

Solución utilizando streams

El siguiente código muestra como realizar la misma búsqueda del número menor utilizando streams.

public static void main(String[] args) {
	List numbers = Arrays.asList(7, 2, 3, 100, 200, 300, 400, 5, 1);
	Integer minValue = numbers.stream().min(Comparator.naturalOrder()).get();
	System.out.println(minValue);
}

Como se puede ver el código anterior es mucho más simple y mucho más fácil de leer, si deseamos buscar el elemento mayor solo debemos ejecutar lo siguiente:

public static void main(String[] args) {
	List numbers = Arrays.asList(7, 2, 3, 100, 200, 300, 400, 5, 1);
	Integer maxValue = numbers.stream().max(Comparator.naturalOrder()).get();
	System.out.println(maxValue);
}

El código anterior mostrará el elemento mayor en la lista.

Para enterarte sobre futuros posts te recomendamos seguirnos en nuestras redes sociales: https://twitter.com/geeks_mx y https://www.facebook.com/geeksJavaMexico/.

Autor: Alejandro Agapito Bautista

Twitter: @raidentrance

Contacto:raidentrance@gmail.com