¿Qué es algo que casi nadie sabe acerca el lenguaje Java?

Java tiene varias curiosidades no tan conocidas, sobre todo si nos ponemos a explorar las classes (class como mecanismo de abstracción en Java). Para esta respuesta sólo demuestro dos curiosidades:
  1. Cada .class en Java comienza con el magic number 0xCAFEBABE; esto es, los primero 4 bytes de todos los .class.
  2. A la Python, el primer argumento de un método en Java puede ser la referencia this.

Para comprobar el punto (1) me he creado este método:
  1. // magic number 
  2. // chequea si los 4 primeros bytes del .class  
  3. // cooresponden con 0xCAFEBABE 
  4. static boolean checkMagicNumber() throws IOException 
  5. { 
  6. Class<?> cls = Main.class; 
  7. byte[] magicBytes = new byte[4]; 
  8. // leer los 4 primeros bytes de Main.class 
  9. cls.getResourceAsStream(cls.getSimpleName() + ".class") 
  10. .read(magicBytes); 
  11.  
  12. // ByteBuffer me ahorra algo de trabajo manual 
  13. // manipulano bits mecanicamente 
  14. ByteBuffer magicNumberAsBytes = 
  15. ByteBuffer.allocate(magicBytes.length); 
  16.  
  17. // de int (0xCAFEBABE) a ByteBuffer 
  18. magicNumberAsBytes.putInt(0xCAFEBABE); 
  19. // comparar 
  20. return 0 == Arrays.compare(magicNumberAsBytes.array(), 
  21. magicBytes); 
  22. } 
Básicamente lo que hace es leer los cuatro primeros bytes de la misma clase que estoy ejecutando, luego convierto el numero (mágico) que quiero comparar a byte[] y comparo ambos!

Para el punto (2) me he creado una clase Point con varios método donde declaro como primer parámetro la referencia a this. A diferencia de Python, claro, this debe ser declarado con su tipo de dato, para la clase sería Point this;. Poder declara el parametro de esta forma no es una simple curiosidad, existe una justificación y es poder decorar el parametro con una annotation para lo cual me creé un annotation dummy @Const (que no hace nada) para demostrar el uso. Este feature se llama reciever parameter. .
El primer método de Point donde utilizo la declaración es repr:
  1. // this reference como parametro explicito 
  2. private String repr(Point this){ 
  3. return String.format("%s(%.1f, %.1f)",  
  4. this.getClass().getSimpleName(),  
  5. this.x, this.y); 
  6. } 
Nota que no es posible como en Python hacer algo como esto: Point.repr(new Point(/*…*/));. Para Java el método repr no toma ningún argumento y es equivalente a:
  1. private String repr(){ /* misma definicion */} 
En el próximo método si uso la annotation y además un parametro “adicional”:
  1. // distancia entre dos puntos  
  2. double distance(@Const Point this, @Const Point other){ 
  3. return Math.hypot(x + other.x, y + other.y); 
  4. } 
Otra vez, si tengo dos Points pnt1 y pnt2 el método se llama así pnt1.distance(pnt2); como se dijo antes el primer parametro (el this explícito se ignora).
Nota: si quisiera declarar en una clase interna que un método acepta un parámetro explícito de la clase externa se haría así:
  1. class Out{ 
  2. /* metodo y definiciones de Out */ 
  3.  
  4. // clase interna 
  5. class In{ 
  6. /* otros métodos */ 
  7. /* ... */ 
  8. public void show(Out Out.this) { /* definición */} 
  9. } 
  10. } 
Out.this es el nombre de la referencia que es de tipo Out por eso es Out Out.this.
El main de mi ejemplo:
  1. public static void main(String[] args) 
  2. { 
  3. var coord1 = Point.of(4.1,8.5); 
  4. var origen = Point.of(0.0, 0.0); 
  5.  
  6. out.format("coord1: = %s%n", coord1); 
  7. out.format("Distancia al origen = %.1f%n", 
  8. origen.distance(coord1)); 
  9. try{ 
  10. out.format( 
  11. "Cada .class comienza por 0xCAFEBABE = %b%n", 
  12. checkMagicNumber()); 
  13. } 
  14. catch(IOException ioe){} 
  15. } 
Salida:
  1. coord1: = Point(4.1, 8.5) 
  2. Distancia al origen = 9.4 
  3. Cada .class comienza por 0xCAFEBABE = true

Notas al pie:
Chapter 8. Classes

Publicar un comentario

0 Comentarios