Skip to main content

Anonymous Classes

· 2 min read
Axel Berlot
Java developer @ Opendev Pro

banner

sandbox.molokotech.com are powered by the Molokotech.

Las clases anónimas son sólo objetos que se instancian en función a una interface, una clase abstracta o una clase concreta que permite definir una clase conreta dentro de una instancia y sólo dentro de la misma, de esta forma no es necesario crear toda una clase nueva para un comportamiento custom.

Ejemplo

  • Abajo una clase anónima en función de Car que retorna un String
  • Debajo una clase que hereda la clase Car.
import java.time.LocalDateTime;

public class CustomCar {
public static void main(String[] args) {
// We define a custom class from another one that only going to live inside myCustomCar
String messageFromCustomCar = new Car() {

// Custom attribute
String brand = "Chevrolet";

// This method is useless but it is needed to override
@Override
void startedTime() {
System.out.println("started ON second: " + LocalDateTime.now().getSecond());
}

// Custom method
String getMessage() {
return "Do you like my " + brand + " custom Car?";
}

}.getMessage();

// We only print getMessage()
System.out.println(messageFromCustomCar);

}
}

// Classic extension
class Peugeot extends Car {
@Override
void startedTime() {
System.out.println("Started ON pretty fast");
}
}

// Could be an Interface, a concrete class or an abstract class
abstract class Car {
abstract void startedTime();
}

Cuándo y dónde utilizarla

  1. Utilizar una definición en un punto en particular para no tener que definir toda una clase completa para evitar tener clases definidas que no vas a reutilizar.
  2. Tener exactamente la implementación justo donde se use por ejemplo en aquellos parámetros que son interfaces.
  3. Acceder a miembros de la clase que existen en la clase custom.

public class CustomCar {

public static void main(String[] args) {
// In this case Runnable is an Interface
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("Just in the exact place");
}
});
}

}

tip

Hoy en día se utilizan menos las clases anónimas ya que aprarecieron las expresiones lambda, sin embargo las mismas tienen la desventaja que sólo pueden ser definidas si:

  1. De donde se crean es una interface.
  2. Si sólo tiene un método abstracto que no sea los que vienen de la clase Object.

Interfaces

· 4 min read
Axel Berlot
Java developer @ Opendev Pro

banner

sandbox.molokotech.com are powered by the Molokotech.

Características:

  • Todos sus métodos son públicos por defecto, pero luego de java 9 en adelante los mismos pueden ser definidos privados con body definido.
  • Pueden contener atributos y métodos.
  • Pueden extender de otras interfaces.
  • Pueden todos sus atributos son abstractos.
  • Sus atributos son finales por definición.
  • Pueden contener atributos y métodos estáticos.
  • Desde Java 8 en adelante pueden contener uno o más métodos default.

Ventajas:

  • Las interfaces nos permiten definir contratos, que luego son implementados en una clase concreta.
  • Las interfaces nos permiten aplicar con flexibilidad la abstracción y el polimorfismo.
  • Una Clase puede implementar múltiples interfaces que a diferencia de herencia entre clases que sólo se puede heredar de una clase.
  • Las interfaces pueden ayudarnos a tener un código más resumido con el uso de expresiones lambda.
  • En función a interfaces pueden crearse instancias de clases anónimas para no depender de una implementación o clase particular previamente definida.
// Concrete implementation
public class HiloConcreto implements Startable {

@Override
public void start() {
System.out.println("HiloConcreto started");
}

}

// Anonymous class
class MainAnonymous {
public static void main(String[] args) {
Startable s = new Startable() {

@Override
public void start() {
System.out.println("Main started into anonymous class!!!");
}

};
s.start();
}
}

// Lambda expression
class MainLambdaExpression {
public static void main(String[] args) {
Startable s = () -> System.out.println("Main started into lambda expression!!!");
s.start();
}
}

interface Startable {
void start();
}

Caracterísitcas de sus miembros:

  • Los atributos son públic, estáticos y finales siempre.
  • Los métodos pueden ser públicos por defecto o privados.
  • Los métodos pueden ser abstractos o croncretos.
  • Los métodos cuando son abstractos no pueden ser estático y vecebersa.
  • El uso de un método default tiene sentido cuando en las implementaciónes no se sobreescribe el mismo.
  • Las insterfaces pueden definir múltiples métodos default.
  • Pueden definir múltiples métodos privados.
  • El uso de métodos privados tiene sentido cuando internamente en la misma interface se invocan métodos.
  • Como cualquier acceso estático se puede prescindir del nombre de la clase, a menos que exista una variable definida en el mismo método con lo cual el compilador prioriza la variable local y deja de apuntar a la estática.

Ejemplo práctico de cada uso

El siguiente es un ejercicio, donde se trata de explicar cómo se pueden usar los miembros de la interface en forma práctica.

public interface Streamable {

/**
* Attributes -> only public
*/

// implicitly public static and final
String letters = "abc";
// Redundant static and access modifier
public static String numbers = "123";

/**
* Methods -> can be public or private
*/

// implicitly public and abstract
void stream();

// Redundant abstract and access modifier
public abstract void streamRedundant();

// Must've a body too
private void privateInstanceLetterMethod() {
letters = letters + "def"; // this will not compile!!!
System.out.println(Streamable.letters);
}

// Belongs to the instance when the implementation did not override this one
default void defaultLettersBehaivor() {
privateInstanceLetterMethod();
}

// Belongs to the instance when the implementation did not override this one
default void defaultNumbersBehaivor() {
Streamable.privatEstaticMethod("678");
}

// Belongs to the class and must've a body too
private static void privatEstaticMethod(String numbers) {
numbers = numbers + "456"; // this will compile OK!!!
System.out.println(Streamable.numbers);
}

}
tip

Desde Java 8 en adelante se habilitó el uso de expresiones lambda ampliamente utilizadas en programación funcional, la que mejora notablemente la lectura del código y limita la cantidad de líneas de código haciendo una lectura de los códigos fuente más legible.

Autoboxing and Unboxing

· 3 min read
Axel Berlot
Java developer @ Opendev Pro

banner

Autoboxing is the conversion from a primitive value to a Wrapper Classes. It is a background operation outside from de programmer control, that transform primitive types creating objects without explicity creating in the code.

The simplest way:
Integer i = 1;

From behind the scenes compiler run new Integer(1) although the IDE says it's deprecated when you type it, the intention is to write cleaner code.

It runs when:

  1. Passed as a parameter to a method that expects a value of the corresponding object wrapper type.
  2. Assigned to a variable of the corresponding object wrapper type.
package com.molokotech.models;

public class Autoboxing {

// Assigned to a variable
void number() {
int number = 100;
Integer num = number; // The compiler converts from int to new Integer
}

// Passed as a parameter
void iterate() {
List<Integer> li = new ArrayList<>();
for (int i = 1; i < 50; i)
li.add(i); // The compiler converts from int to new Integer
}

// Simplest examples
void autoboxing() {
Character c = 'c'; // char to new Character
Integer i = 1; // int to new Integer
}

/* remainder (%) and unary plus (+=) operators does'nt works with:
Integer
Byte
Short
Only primitive types are allowed for this operators so from behind the scenes always invokes i.intValue() */
public int sum(List<Byte> li) {
int sum = 0;
for (Byte i : li)
if (i % 2 == 0)
sum += i;
return sum;
}

}

autoboxing

Unboxing is the conversion from an Object to a primitive value.

The simplest way:
Integer number = 100; int num = number;

  1. Passed as a parameter to a method that expects a value of the corresponding primitive type.
  2. Assigned to a variable of the corresponding primitive type.
package com.molokotech.models;

public class Unboxing {

// Assigned to a variable
void number() {
Integer number = 100;
int num = number; // The compiler convertes from Integer to int
}

// Passed as a parameter
void iterate() {
int[] li = new int[50];
for (Integer i = 1; i < 50; i += 2)
li[i] = i;
}

}

unboxing

Java compiler run the background operation with for autoboxing and unboxing on the corresponding wrapper clases:

Primitive typeWrapper class
booleanBoolean
byteByte
charCharacter
floatFloat
intInteger
longLong
shortShort
doubleDouble

Access modifiers

· 3 min read
Axel Berlot
Java developer @ Opendev Pro

banner

Access modifiers are implemented in java with four levels of abtraction.

Improves:

  • Security
  • Cleaner code
  • Scalability
  • Readability

They are aplicable to:

  1. Classes → public | default
  2. Interfaces → public | default
  3. Constructors → public | default | protected | private
  4. Class member attributes and methods → public | default | protected | private
  5. Interface member attributes and methods → public | private

Level of visibility:

  • public → could be access from every pacakge
  • default → inside the same package only (no keyword)
  • protected → access from the same package and their sub classes
  • private → only inside the class

Defaultrefers at no reserve keyword just, it isn't the keyword of the example bellow because this refers to the type of member method

Classes and Interfaces could be public or default (no keyword).

Interfaces cannot have members with default or protected access modifiers, please remember that the default modifier it's not the keyword.

Real life example for access modifiers in an interface:

package com.molokotech.interfaces;

// Accesible from any package
public interface Loggueable {
void log(); // implicitly public by default write public does not affect

// private access modifier allowed but should have body
private String colorConsole(String color) {
switch(color) {
case "yellow":
return "WARN";
case "red":
return "ERROR";
case "blue":
return "INFO";
default:
return "INFO";
}
};

/* default methods cannot be private are always public or implicity public
every instance implementation should be access to it */
default void labelColor(String color) {
colorConsole(color);
}
}

// Accesible with in the same package
interface AnotherLoggeable {

}

Member attributes and methods on the class can carry all types of modifiers.

public class AccessModifiers {

public int n1;
protected int n2;
int n3;
private int n4; // accesible only somewhere inside the class

public static void main(String[] args) {
System.out.println("HOLA");
}

public AccessModifiers() {

}
}

class Protected {
protected Protected() {
new Private(); // this NOT COMPILE at all! cannot access to the constructor outside the class
}

}

class Default {
Default() {
new Protected();
}
}

class Private {
private Private() {
new Default();
}
}

It is important to note that some modifiers affects the overriding behavor.

Data Types

· 5 min read
Axel Berlot
Java developer @ Opendev Pro

banner

sandbox.molokotech.com are powered by the Molokotech.

Java has an strong dependency with data types, it is important to understand how they works and how to use it.

Have you ever tougth why there are so many data types from different sizes? it's really easy to explain, just because when Java was created sitting on 1995 and years later, the devices were Java could installed were pretty small in memory, so the aproach to used only data types that reserves less memory was important.

Today no one is concerned by this because your phone today is faster and bigger about memory speaking than your computers.

Today it's not a problem if you choose to use a byte or an int because it does'nt have side effects on the devices.

Data Types in Java.

package com.molokotech.models;

public class DataTypes {
private int num = 1; // Integer wrapper Class
private boolean isBigger = true; // Boolean wrapper Class
private char ch = 'j'; // Character wrapper Class
private float f = 100; // Float wrapper Class cast needed 100f/100F
private long l = 5000; // Long wrapper Class cast needed 5000l/5000L
private short sh = 1234; // Short wrapper Class
private byte b = 0; // Byte wrapper Class

// private vars can access to methods from the same class
private void getDataType() {
System.out.println("primitive type " + this.num + " in Java is the default for all non decimal numbers");
System.out.println("primitive type " + this.isBigger + " default value is false");
System.out.println("primitive type " + this.ch + " should be rounded by simple quotes default value 0 or u0000, character/letter");
System.out.println("primitive type " + this.f + " it's used by default like int default value 0.0");
System.out.println("primitive type " + this.l);
System.out.println("primitive type " + this.sh);
System.out.println("primitive type " + this.b + " values from -128/127");
}

// getDataType() should be called only from this class
public static void main(String[] args) {
new DataTypes().getDataType();
}
}

result

Wrapper classes Float and Long shoud be casted when declared until it reference another variable, in this momment compile apply autoboxing strategy.

package com.molokotech.models;

public class Cast {

private float f = 100; // Float wrapper Class cast needed 100f/100F
private long l = 5000; // Long wrapper Class cast needed 5000l/5000L

private void doCast() {
Float sh = 123f; // must be casted -> Float sh = 123; not compile!!
Float shRef = this.f; // Autoboxing when ref another variable not casting needed

Long l = 123l; // must be casted -> Long l = 123; not compile!!
Long lref = this.l; // Autoboxing when ref another variable not casting needed
}

}

There three rules:

  1. Every number data-types non-decimal is treated as an int always unless it is explicitly cast.
  2. Every number data-types decimal es treated as an double always unless it is explicitly cast.
  3. Cannot do reference from a smaller data-type to a bigger data-type, because it does'nt fit, unless it is casted, in that case the value will try to fit passing maximun values.

The next image it's not absolute and is just note the different sizes between data types in a graphical way.

result

This code has some COMPILATION PROBLEMS related to bigger data-types trying to fit in smaller or incompatible data-types and other valid code you should copy into your IDE and make an analize it.


package com.molokotech.utils;

public class ReferenceMemory {

void referenceMemoryPoints() {
int num1 = 1000;
Integer num2 = num1; // autoboxing compile

byte num3 = 123;
Integer num4 = num3; // compiler knows that a byte is not an Integer -> not compile

byte num5 = 123;
int num6 = num5; // num5 primitive treated as an int, fit as an int -> compile

short num7 = 1234;
int num8 = num7; // num6 primitive treated as an int, fit as an int -> compile

int num9 = 123;
byte num10 = num9; // num9 does'nt fit in a byte because is bigger unless be casted -> not compile

int num11 = 123;
byte num12 = (byte) num11; // num11 does'nt fit in a byte because is bigger but is explicitly casted -> compile

long num13 = 12000;
Integer num14 = num13; // compiler knows that a long is not an Integer -> not compile

long num15 = 13000;
Integer num16 = (int) num13; // long is not an Integer but just casted -> compile

long num17 = 900000000033333l;
int num18 = num17; // long cannot fit into an Integer -> not compile

long num19 = 900000000033333l;
int num20 = (int) num19; // cannot fit but cast exced limits of the datatype -> compile

int num21 = 2000;
byte num22 = (byte) num21;
System.out.println(num22); // cannot fit but cast exced limits of the datatype -> compile
}

}

Viewing the results in the IDE

result

If we comment the compilation problems and run the program we see that prints a negative number even if we assign a positive number, this is because it reach out the max size of data-type and force it to assign with the cast, so it tries to fit anyway passing the limits.

max