Métodos estáticos en Java y Kotlin para entender Android

Si estás aprendiendo el desarrollo nativo en Android te habrás dado cuenta que se utilizan variables y métodos estáticos con mucha frecuencia. Además si desarrollas en Kotlin te habrás fijado que gran parte de los métodos que utiliza el framework no están implementados en Kotlin si no en Java. Esto es gracias a la interoperabilidad que tienen estos dos lenguajes. Es por eso que para programar en Android el conocimiento de Java a pesar de que trabajes con Kotlin es muy importante.

En este tutorial aprenderemos sobre los métodos estáticos y cómo utilizarlos en Java y Kotlin. También veremos cómo son utilizados dentro de un Fragment en Android. Además aprenderemos sobre el método apply en Kotlin.

Índice

  1. Qué es una variable estática
  2. Qué es un método estático
  3. Variables y métodos estáticos en Kotlin
  4. Ejecutar un método estático Kotlin desde Java
  5. Métodos estáticos en un Fragment de Android
  6. Cambiar valores usando apply

1. Qué es una variable estática

Las variables estáticas son un tipo especial de variables que como su nombre indica son estáticas ya que no se crean dinámicamente con cada objeto o instancia de la clase a la que pertenecen. Esto quiere decir que no necesitamos una instancia de clase para utilizar este tipo de variable. Además una vez que esta variable es creada es compartido por todos los objetos o instancias de clase que se hayan creado. Veamos el siguiente ejemplo.

public class Principal {
    public static void main(String []args){
        System.out.println(MyClass.name);
    }
}

class MyClass {
    static String name = "AndyGeek";
}
# Output
AndyGeek

En el ejemplo anterior tenemos una variable estática dentro de la clase MyClass y como vemos no es necesario crear un objeto de dicha clase para tener acceso a la variable estática name. Es por esto que se dice que una variable estática pertenece a la clase y no a la instancia de la clase. Veamos otro ejemplo donde ilustramos que la variable estática se comparte por los distintos objetos de la clase.

public class Principal {
    public static void main(String []args){
        MyClass obj1 = new MyClass();
        obj1.print();
        System.out.println("---------------");
        MyClass obj2 = new MyClass();
        obj2.print();
        System.out.println("---------------");
        MyClass obj3 = new MyClass();
        obj3.print();
    }
}

class MyClass {
    int var1 = 0;
    static int var2 = 0;

    MyClass(){
        var1 += 5;
        var2 += 5;
    }

    public void print(){
        System.out.println(var1);
        System.out.println(var2);
    }
}
# Output
5
5
---------------
5
10
---------------
5
15

El ejemplo anterior compara una variable estática con una variable normal de la clase MyClass creando tres objetos(obj1, obj2, obj3) de esta clase. El constructor de esta clase al momento de crear el objeto aumenta 5 unidades al valor inicial de 0 que tienen las variables. Luego de crear los tres objetos y mandar a imprimir el valor de las variables en pantalla vemos como la variable estática va subiendo de valor en cada creación. Esto nos dice que es una única variable se está compartiendo entre los tres objetos que se crearon.

Pues bien, ahora ya sabemos que es una variable estática. Los casos de uso pueden ser muchos. Por ejemplo, el de crear un objeto usuario dentro de una clase que se instancia en múltiples lugares del código. De hecho el patrón Singleton utiliza una variable estática para asegurarse de que se cree una instancia única de la clase.

2. Qué es un método estático

Un método estático cumple las mismas características que la variable estática. En decir que forma parte de la clase y no de la instancia de la clase por lo que no es necesario crear un objeto o instancia de clase para utilizarlo. Veamos el siguiente ejemplo.

public class Principal {
    public static void main(String []args){
        MyClass.main();
    }
}
class MyClass {
    static void main(){
        System.out.println("Hello");
    }
}
# Output
Hello

En el ejemplo anterior ejecutamos un método estático y como ves no fue necesario crear un objeto o instancia de clase para utilizarlo. Otra característica que debe cumplir un método estático es que solo puede llamar a variables estáticas o métodos estáticos. Esto parece lógico verdad, ya que las variables normales no pueden llamarse sin antes haberse creado mediante una instancia. Veamos un ejemplo.

public class Principal {
    public static void main(String []args){
        MyClass.print();
    }
}
class MyClass {

    static String name = "AndyGeek";

    static void print(){
        System.out.println("Hello " + name);
    }
}
# Output
Hello AndyGeek
También es importante mensionar que un método estático al no tener instancia de clase no puede utilizar tanto this, que hace referencia a la instancia de la clase, o super, que hace referencia al constructor de la clase padre.

Por ultimo, de seguro te diste cuenta que el método main() que llamamos para iniciar algún proyecto en Java es un método estático. Esta es la razón por la que nuestro compilador llama a ese método sin necesidad de crear un objeto de la clase que lo contiene.

public static void main(String []args){

}

3. Variables y métodos estáticos en Kotlin

En Kotlin las cosas son un poco distintas a Java ya que no tenemos variables estáticas por defecto dentro del lenguaje. Así que si queremos una variable que pueda ser accesada sin la necesidad de crear un objeto su clase debemos declarar dicha variable dentro de companion object { } como se muestra en el siguiente ejemplo.

class MyClass {
    companion object {
       var name : String = "AndyGeek"
    }
}
fun main(){
    println(MyClass.name)
}
# Output
AndyGeek

Como ves en el ejemplo anterior, los miembros del companion object pueden ser accesados sin necesidad de una instancia de clase. Además cumple las mismas propiedades de una variable estática Java, como la propiedad de las instancias de la clase de compartir variable estática. Veamos el siguiente ejemplo, muy similar al que hicimos en Java.

class MyClass {
    var var1 : Int = 0
    companion object {
       var var2 : Int = 0
    }
    constructor() {
        var1 += 5
        var2 += 5
    }
    fun print(){
        println(var1)
        println(var2)
    }

}
fun main(){
    var obj1 = MyClass()
    obj1.print()
    println("--------")
    var obj2 = MyClass()
    obj2.print()
    println("--------")
    var obj3 = MyClass()
    obj3.print()
}
# Output
5
5
--------
5
10
--------
5
15

Pues bien ahora ya sabemos cómo podemos declarar variables estáticas en Kotlin. Si lo que queremos ahora es declarar un método estático, pues solo bastaría con colocar el método dentro de companion object { } tal como lo hicimos con las variables. Veamos el siguiente ejemplo.

class MyClass {
    var var1 : Int = 0
    companion object {
        var var2 : Int = 0
        fun printVar2(){
            println("La variable estatica es: "+var2)
        }
    }
}
fun main(){
    MyClass.printVar2()
}
# Output
La variable estatica es: 0

4. Ejecutar un método estático Kotlin desde Java

Una de las características principales de Kotlin es que es totalmente interoperable con Java. Pues bien gracias a esto nosotros podemos crear una función en Java que utilice una clase en Kotlin y, por que no, que pueda utilizar un método estático de Kotlin. Esto es frecuente en Android ya que nosotros crearemos nuestras clases en Kotlin. Pero, por detrás serán funciones en Java las que ejecutarán y nuestras clases en Kotlin.

Sin embargo, si creamos una clase en Kotlin con un método estático usando el companion object y luego intentamos ejecutar ese método desde un archivo Java, obtendremos un error. Esto es por que Java no entiende por defecto al método companion object y Kotlin tampoco hace nada por hacer que Java entienda ya que el compilador presupone que solo trabajaremos con Kotlin.

La forma de hacer que Kotlin prepare en su compilación al método estático para que pueda ser entendido desde Java es usando la anotación @JvmStatic encima de la función estática que usaremos en Java. Veamos el siguiente ejemplo.

// Código Kotlin
class MyClass {
    var var1 : Int = 0
    companion object {
        var var2 : Int = 0
        @JvmStatic
        fun printVar2(){
            println("La variable estatica es: " + var2)
        }
    }
}
// Código Java que ejecuta el método estático en Kotlin
public class principal {
    public static void main(String []args){
        MyClass.printVar2();
    }
}

Esta es la razón por la cual veremos esta anotación @JvmStatic encima de muchas funciones dentro de Android si utilizamos Kotlin como lenguaje.

5. Métodos estáticos en un Fragment de Android

Al crear un Fragment en blanco Fragment(Blank) en Android usando Java o Kotlin este se crea junto con una serie de métodos, dentro de la clase que lo controla, que ya vienen implementados desde un inicio. Estos métodos nos ayudan a instanciar el Fragment utilizando un patrón llamado newInstance. Veamos cómo están implementados este método.

public static MyFragment newInstance(String param1, String param2){
    MyFragment fragment = new MyFragment();
    Bundle args = new Bundle();
    args.putString(ARG_PARAM1, param1);
    args.putString(ARG_PARAM2, param2);
    fragment.setArguments(arg);
    return fragment;
}

Básicamente este método crea una nueva instancia de nuestro Fragment y es utilizado para eso, crear instancias de nuestro Fragment. ¿Por qué lo hacemos de esta manera y no con el constructor del Fragment? Pues esto es necesario porque es la única manera de pasar parámetros al Fragment cuando lo creamos. Ya que el Framework Android necesita de un constructor vacío para los Fragments y eso lo vemos más arriba en el código autogenerado.

public MyFragment() {
    // Required empty public constructor
}

Entonces para ilustrar mejor este punto veamos cómo crearíamos un objeto de una clase en Java usando el patrón newInstance que acabamos de ver.

class Fragment
{
    static String name;
    public static Fragment newInstance(String param)
    {
        Fragment fragment = new Fragment();
        name = param;
        return fragment;
    }
    public Fragment(){
        // Required empty public constructor
    }
    public void print(){
        System.out.println(name);
    }
}

class Principal
{
    public static void main(String args[])
    {
        Fragment obj = Fragment.newInstance("AndyGeek");
        obj.print();
    }
}
# Output
AndyGeek

Como vimos, al momento de crear una instancia del Fragment utilizamos el newInstance en lugar el tradicional new Fragment() y para que newInstance() pueda funcionar necesita ser estático ya que lo usamos para crear una instancia de Fragment y en ese momento no tenemos ninguna instancia del Fragment creada. Ahora veamos cómo se soluciona esto usando Kotlin.

companion object {
    @JvmStatic
    fun newInstance(param1: String, param2: String) = FirstFragment().apply {
        arguments = Bundle().apply {
            putString(ARG_PARAM1, param1)
            putString(ARG_PARAM2, param2)
        }
    }
}

Vemos que Kotlin utilizan companion object, para declarar el método estático newInstance el será leído desde código Java (El framework de Android) este tiene la anotación @JvmStatic, que ya vimos en este tutorial.

Listo!! acabamos de ver un ejemplo de método estático en Java y Kotlin dentro de Android. Pero ya que estamos comparando los métodos newInstance() de Java y Kotlin nos encontramos con una diferencia más, este es el uso de apply en Kotlin. Asi que a continuación veremos para qué sirve y cómo se utiliza este método.

6. Cambiar valores usando apply

Si nos fijamos en el código anterior descubriremos que en Kotlin para cambiar el valor de arguments en lugar de usar setArguments() como en Java, Kotlin utiliza apply. Esta es una forma de cambiar el valor de las variables o ejecutar métodos de manera más directa, creando la instancia de la clase pero de manera implícita. Veamos el siguiente ejemplo.

class MyClass {
    var var1 : Int = 0
    var var2 : Int = 0


    constructor() {
        this.var1 = 5
        this.var2 = 5
    }
    fun print(){
        println(var1)
        println(var2)
    }


}
fun main(){
    var obj = MyClass()
    obj.apply {
        this.var1 += 5
    }
    obj.print()
}
# Output
10
5

En el ejemplo anterior vemos cómo cambiar el valor de una variable usando apply en un objeto, pero como dijimos anteriormente podemos crear el objeto de manera implícita y cambiar el valor de la variable en la misma sentencia. Veamos cómo podemos hacer esto.

fun main(){
    var obj = MyClass().apply {
        this.var1 += 5
    }
    obj.print()
}

Bien, ya nos estamos acercando a lo que vemos en Android. Ahora que pasaría si queremos que una función sea el que devuelva el objeto que creamos tal como lo hace newInstance(), entonces podemos hacer lo siguiente.

class MyClass {
    var var1 : Int = 0
    var var2 : Int = 0


    constructor() {
        this.var1 = 5
        this.var2 = 5
    }
    fun print(){
        println(var1)
        println(var2)
    }
}
// Retorna la instancia de MyClass
fun getMyClass() = MyClass().apply {
    this.var1 += 5
}

fun main(){

    var objMyClass = getMyClass();
    objMyClass.print();

}
# Output
10
5

En el ejemplo anterior creamos un método que nos devuelve una instancia de la clase de manera directa y a la vez utiliza apply para cambiar el valor de una variable, devolviendo así la instancia con su variable cambiada. Luego usamos esta instancia para crear una variable objMyClass que contiene la instancia de la clase dentro de main().

Ahora comparemos el método getMyClass() de este código con el código de newInstance() que nos genera Android.

fun newInstance(param1: String, param2: String) = MyFragment().apply {
    arguments = Bundle().apply {
        putString(ARG_PARAM1, param1)
        putString(ARG_PARAM2, param2)
    }
}

Pues bien, se parecen mucho verdad. Esto es porque esa es la forma que Android utiliza para cambiar el arguments y así retorna la instancia del Fragment de manera directa. Además utiliza apply para ejecutar dos putString() en un objeto nuevo de Bundle() que es como se instancia arguments. No es difícil de entender solo haz el esfuerzo y compara ambos código teniendo en cuenta lo que ya sabemos sobre apply.

Y llegamos al final de este tutorial Ahora ya sabes un poco más sobre el código autogenerado de un Fragment en Android. En siguientes tutoriales iremos descubriendo más acerca de cómo funciona el desarrollo nativo ya sea con Kotlin o Java. 😃