Software Development Notions

Principios fundamentales de los tipos de datos en Java

June 26, 2020

Primitivos y wrappers de primitivos en Java

Asignar una variable primitiva usando otra variable primitiva

Con los primitivos, cuando asignamos el valor de una variable a el valor de otra variable, simplemente se genera una copia, por lo que la variable original no mutará su estado por más que la variable nueva decida cambiar.

public class Main {
    public static void main(String[] args) {
        int x = 0;
        int y = x;
        y=5;
        System.out.println(x);//x sigue valiendo 0
    }
}

Funciones que operan primitivos

Algo similar ocurre cuando se pasa por parámetro a una función un tipo primitivo, pasa a ser una copia, por lo que es una variable nueva dentro de dicho scope.

public class Main {
    public static void main(String[] args) {
        int x = 0;
        Numbers numbers = new Numbers();
        numbers.integers(x);
        System.out.println(x);//x sigue valiendo 0
    }
    
}
class Numbers{
    public static void integers(int number){
	    number = 5;
    }
}

Asignar una variable tipo wrapper primitivo usando otra variable del mismo tipo

Con los no primitivos, cuando asignamos el valor de una variable a el valor de otra variable, simplemente se genera una copia, por lo que la variable original no mutará su estado por más que la variable nueva decida cambiar.

public class Main {
    public static void main(String[] args) {
        Integer x = 0;
        Integer y = x;
        y = 10;
        System.out.println(x);//x sigue valiendo 0
    }
    
}

Comparando objetos wrappers (que envuelven primitivos)

Comparar los primitivos mediante un simple ”==” es seguro, se va a comportar siempre como esperamos.

public class Main {
    public static void main(String[] args) {
        int x = 0;
        int y = x;
        System.out.println(x == y);
        //el resultado es true
    }
}

El problema surge cuando en lugar de usar los primitivos, usamos la clase correspondiente que lo envuelve y que ofrece métodos para operar con él. En el siguiente ejemplo, vemos como diferentes creaciones de un String dan diferentes resultados a la hora de comparar.

public class Main {
    public static void main(String[] args) {
        String s1 = new String("campusMVP");
        String s2 = new String("campusMVP");
        System.out.println(s1 == s2);   //Devuelve false
    /*----------------------------------------------------*/
        String s3 = "campusMVP";
        String s4 = new String("campusMVP");
        System.out.println(s3 == s4);   //Devuelve false
    /*----------------------------------------------------*/
        String s5 = "campusMVP";
        String s6 = "campusMVP";
        System.out.println(s5 == s6);   //Devuelve true
    }
}

La comparación mediante ”==” no siempre funciona como esperamos, por lo que lo mejor sería usar en su lugar “equals()“.

public class Main {
    public static void main(String[] args) {
        String s1 = new String("campusMVP");
        String s2 = new String("campusMVP");
        System.out.println(s1.equals(s2));   //Devuelve true
    /*----------------------------------------------------*/
        String s3 = "campusMVP";
        String s4 = new String("campusMVP");
        System.out.println(s3.equals(s4));   //Devuelve true
    /*----------------------------------------------------*/
        String s5 = "campusMVP";
        String s6 = "campusMVP";
        System.out.println(s5.equals(s6));   //Devuelve true
    }
}

No solo cuenta que es más seguro realizarlo de ésta forma, además dejas más clara la intención de estar haciendo una comparación por el valor del objeto.

Objetos propios

Funciones que operan nuestros objetos

Anteriormente, vimos que los primitivos, o las clases que los usan mejor dicho, ejecutan métodos que devuelven copias modificadas del original. Cuando tenemos nuestros propios objetos la historia cambia. Según como desarrollemos sus métodos, podrá alterar su estado inicial, o simplemente devolver copias tal que lo hacen los primitivos.

En el siguiente ejemplo, podemos ver ciertos métodos que cambian el estado original del objeto, porque nuestro propio método en este caso si muta el estado original.

public class Main {
    public static void main(String args[]) {
      SomeType someType = new SomeType();
      SomeType another = new SomeType();
      someType.firstMethod(another); // another{ num:5, text:null }
      System.out.println(another.toString());
    }
}
class SomeType{
    public int num;
    public String text;
    public void firstMethod(SomeType someType){
	        someType.num=5; // hace referencia al mismo objeto recibido por parámetro
    }
    @Override
    public String toString(){
        return "{num:" + num + ", " + "text:" + text + "}";
    }
}

En cambio podemos provocar que ocurra algo similar que con los primitivos.

public class Main {
    public static void main(String args[]) {
      SomeType someType = new SomeType();
      SomeType another = new SomeType();
      someType.firstMethod(another); // another{ num:0, text:null }
      System.out.println(another.toString());
    }
}
class SomeType{
    public int num;
    public String text;
    public void firstMethod(SomeType someType){
            someType = new SomeType(); // ya NO hace referencia al mismo objeto recibido por parámetro
	        someType.num=5; 
    }
    @Override
    public String toString(){
        return "{num:" + num + ", " + "text:" + text + "}";
    }
}

Funciones que operan colecciones o arrays de nuestros objetos

Otro caso más específico que debemos tener en cuenta, sería el hecho de que los arrays de primitivos, si mutan su valor.

public class Main {
    public static void main(String args[]) {
      SomeType someType = new SomeType();
      SomeType another = new SomeType();
      someType.firstMethod(another.num);
      System.out.println(another.num[0]); // 5
    }
}
class SomeType{
    public int [] num = new int[5];
    public String text;
    public void firstMethod(int[] num){
        num[0] = 5;
    }
}

Tipos de funciones

command query separator (cqs)

Es un principio que nos dice que debemos de diferenciar entre dos tipos de funciones: por un lado, tenemos las “querys”, que no alteran el estado del sistema

c = sum(a,b); *//realiza una "pregunta" y asigna la respuesta a una variable.*

; por otro lado, tenemos las “command”, que sí alteran el estado del sistema

sum(a,b); *//internamente, está asignando el resultado de la operación a algún campo, por lo que sería una "orden".*

Una vez conocida la diferencia, cabe recordar evitar la mezcla de éstos dos tipos de funciones, de tal modo que, ni una “query” debe alterar el estado del sistema, y una “command” se limita a operar con datos del sistema. Por ejemplo, un value object contiene solo funciones tipo command.

Funciones puras

Son esas funciones que no dependen de información externa para su ejecución. Realizar funciones los mas puras posibles, nos garantiza flexibilidad, además de que esa función puede estar en cualquier sitio, y que por cambiarla no romperemos nada. Aquí vemos un ejemplo, en el que por más que llamemos a la función, si siempre es el mismo parámetro, el resultado será el mismo.

`class SomeType{ public double raizCuadrada(int n){ return Math.sqrt(n); }}`


Welcome to my blog about Software Development! I would like to invite you to learning with me 👨‍💻

Search all posts