Syntax and generics

Java syntax sugar

Syntax sugar, also known as sugar coated grammar, is a term invented by British computer scientist Peter J. landin. It refers to a grammar added to the computer language. This grammar has no impact on the function of the language, but it is more convenient for programmers to use. The most commonly used syntax sugars in Java mainly include generics, variable length parameters, conditional compilation, automatic disassembly box, internal classes, etc. Virtual machines do not support these grammars. They are restored to a simple basic syntax structure at the compilation stage, which becomes an interpreter sugar.

Generics is a new feature introduced after JDK1.5. When generics do not appear in the Java language, the function of generics can only be realized through the combination of the two characteristics that Object is the parent class of all types and type coercion. In this way, the realized generic function can only know the real Object type of Object during the program run time. In the javac compilation time, The compiler cannot check whether the forced transformation of this Object is successful, which transfers some risks to the program runtime.

The generics introduced by the Java language after JDK1.5 actually only exist in the program source code. In the compiled bytecode file, they have been replaced with the original native type, and the forced transformation code has been inserted in the corresponding place. Therefore, for the runtime Java language, ArrayList and ArrayList are the same class. Therefore, generic technology is actually a syntax sugar of Java language. The generic implementation method in Java language is called type erasure, and the generic implemented based on this method is called pseudo generic.

Here is a simple Java generic code:

Map<Integer,String> map = new HashMap<Integer,String>();  
map.put(1,"No.1");  
map.put(2,"No.2");  
System.out.println(map.get(1));  
System.out.println(map.get(2));  

After compiling this Java code into a Class file and decompiling it with the bytecode decompiler, you will find that generics have changed back to native types, as shown in the following code:

Map map = new HashMap();  
map.put(1,"No.1");  
map.put(2,"No.2");  
System.out.println((String)map.get(1));  
System.out.println((String)map.get(2));  

To describe the erasure type in more detail, look at the following code:

import java.util.List;  
public class FanxingTest{  
    public void method(List<String> list){  
        System.out.println("List String");  
    }  
    public void method(List<Integer> list){  
        System.out.println("List Int");  
    }  
}  

When compiling this code with javac compiler, the following error is reported:

FanxingTest.java:3: Name conflict: method(java.util.List<java.lang.String>) and method

(java.util.List<java.lang.Integer>) Have the same suspect

        public void method(List<String> list){

                    ^

FanxingTest.java:6: Name conflict: method(java.util.List<java.lang.Integer>) and metho

d(java.util.List<java.lang.String>) Have the same suspect

        public void method(List<Integer> list){

                    ^

2 error

Because the as like as two peas List and List are erased and become the same native type List, the signature of the two methods is exactly the same as those of the original methods. In the Class class file structure, it is said that there is no way to get the same signature in the Class file.

Modify the above code as follows:

import java.util.List;  
public class FanxingTest{  
    public int method(List<String> list){  
        System.out.println("List String");  
        return 1;  
    }  
    public boolean method(List<Integer> list){  
        System.out.println("List Int");  
        return true;  
    }  
}  

It is found that the compilation can pass at this time (Note: true and 1 in Java language are not related. They belong to different types and cannot be converted to each other. There is no case where the integer value in C language is non-zero or true). The addition of two different types of return values makes the method overloading successful. Why?

We know that the method signature in Java code only includes the method name, parameter order and parameter type, and does not include the return value of the method. Therefore, the return value of the method does not participate in the selection of overloaded methods. Therefore, it seems redundant to add the return value to overloaded methods. This is indeed superfluous for the selection of overloaded methods, but the problem we need to solve now is to make the above code compile so that the two overloaded methods can reasonably coexist in the same Class file. This depends on the method signature of bytecode, which does not only include the information contained in the method signature in Java code, It also includes method return value and checked exception table. After adding different return values to the two overloaded methods, they can coexist in a Class file because of different bytecode signature.

Syntax sugars such as automatic disassembly boxes and variable length parameters are also restored to their native syntax structures at the compilation stage. Therefore, only their corresponding native types exist in the Class file, which will not be described one by one here.

Keywords: Java Scala

Added by Fruct0se on Sat, 18 Sep 2021 09:59:20 +0300