Curiosidades programando Java

Hoy les voy a mostrar algunas curiosidades que se pueden hacer con código Java que puede que algunas sirvan y algunas pues solo para divertirse:

1.- Crear en línea una colección mediante una clase anónima y un bloque inicializador de instancia en lugar de hacerlo con Arrays.asList:

public static void main(String[] args) {
       
        List<Integer> lista = Arrays.asList(7,9,1);
        lista.add(3);
        System.out.println(lista);
}

El inconveniente de usar Arrays.asList es que debido a que la lista se genera desde la raiz de un arreglo, estos por naturaleza tienen un tamaño definido y no puede redimensionarse por lo tanto la lista generada no soporta la operación add o alguna otra que intente modificar el tamaño y devuelve UnsupportedOperationException.

public static void main(String[] args) {
       
        List<Integer> lista = new ArrayList() {{ add(7); add(9); add(1)}};
        lista.add(3);
        System.out.println(lista);
        System.out.println(lista.getClass());
}

Con la clase anónima es posible seguir modificando la colección creada con la notación de doble paréntesis, los primeros sirven para generar una clase anónima que hereda de ArrayList y los segundos paréntesis sirven para definir un bloque inicializador de instancia en donde invocamos el método add heredado de ArrayList y a diferencia de generar la lista en ase al método Arrays.asList en esta ocasión cualquier metodo que modifique la colección no lanzará la excepción UnsupportedOperationException


2.- Comentario que se ejecuta con el truco de la nueva línea en unicode:

    public static void main(String... args) {  
        // \u000d System.out.println("This Comment Executed!");
    }

El resultado de ejecutar el código anterior es que la línea que se encuentra despues de \u00d se ejecuta pues este símbolo unicode representa una nueva línea para el compilador, asombroso !!!


3.- Uniones o simulación de uniones en tipos genéricos que usan covarianza o contravarianza

public class Prueba<T> {

    static interface Animal {
        default void eat(){
            System.out.println("Eating");
        }
    }
   
    static abstract class Dog {
        public abstract void walk();
    }

    public static <T extends Dog & Animal> void process(T type) {
        type.eat();
    }

    static class Bulldog extends Dog implements Animal{
        public void walk(){
            System.out.println("Dog walks in 4 legs");
        }
    }
   
    public static void main(String[] args) {        
        Prueba.<Bulldog>process(new Bulldog());
    }

}

Como se puede observar en el método estático process dentro del genérico covariante se utiliza el operador & para indicar que hereda y/o implementa alguna clase (abstracta o concreta) y/o interfaces, en palabras mas sencilas el genérico que se necesita debe de ser de algún tipo T que herede la clase abstracta Dog e implemente la interface Animal, en este caso es Bulldog.


Y por hoy creo que hasta aquí, tal vez agregue una segunda parte ...

Comentarios

Opciones de visualización de comentarios

Seleccione la forma que prefiera para mostrar los comentarios y haga clic en «Guardar las opciones» para activar los cambios.

El primero es bastante

El primero es bastante interesante, pero no es recomendado porque arraylist puede inicializarse con una collection lo cual elimina el problema de la mutabilidad:

List<Integer> l = new ArrayList(Arrays.asList(1,2,3));
l.add(4);

No por eso deja de ser interesante. Igual de interesante es el efecto que la doble llave tiene es que permite visualizar más claramente el grafo del objeto.

Por ejemplo, si se tuviera la clase dirección con calle, ciudad, estado, país.

class Address {
    String street;
    City city;
}
class City {
    String name;
    Country country;
}
class Country {
    String name;
}

se podría inicializar así:

Address address = new Address() {{
    street = "Av Mexico #1";
    city = new City() {{
         name = "CDMX";
         country = new Country() {{
               name = "Mexico";
          }};
    }};
}};

Obvio, si en vez de doble llave fuera simple y se pudiera omitir el new y los paréntesis sería más fácil de leer.

Address address = Address {
    street = "Av Mexico #1";
    city = City {
         name = "CDMX";
         country = Country {
               name = "Mexico";
          };
    };
};

Esto es algo que nuevos lenguajes como Ceylon o Go hacen aunque no con bloques de instancia.

En Ceylon los parámetros del constructor se especifican en la declaración de la clase. El constructor es una función y los parámetros de las funciones pueden ser llamados (en mis propias palabras) cambiando paréntesis por llaves:

class Address(String street, City city){}
class City(String name, Country country){}
class Country(String name){}

Address a = Address {
    street = "Av Mex";
    city = City {
        name = "CDMX";
        country = Country {
            name = "Mexico";
        };
    };
};

En Go, la inicialización de estructuras struct se hace mediante el uso de llaves

Ejemplo Go

package main

type Address struct {
        name string
        city City
}
type City struct {
        name    string
        country Country
}
type Country struct {
        name string
}

func main() {
        a := Address{
                name: "Av Mex",
                city: City{
                        name: "CDMX",
                        country: Country{
                                "Mexico",
                        },
                },
        }
}

Y llevando las cosas más lejos aún, en ambos se pueden omitir los nombres, dejando versiones muy fáciles de leer:

Ceylon

value a = Address {
    "Av Mex";
    City {
        "CDMX";
        Country {
                "Mexico";
        };
    };
};     

Go

a := Address{
        "Av Mex",
        City{
                "CDMX",
                Country{
                        "Mexico",
                },
        },
}

Esta es una de las razones por las cuales Javascript se volvió tan popular [citation needed], permitiendo especificar en JSON la estructura de los objetos.

Conceptos relacionados:

- https://en.wikipedia.org/wiki/Referential_transparency
- https://en.wikipedia.org/wiki/Homoiconicity

Imagen de ezamudio

tipo intersección

Ese último ejemplo realmente es un tipo intersección, no tipo unión.