In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-01-28 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Servers >
Share
Shulou(Shulou.com)05/31 Report--
This article introduces the relevant knowledge of "what is the way to implement Java generics". In the operation of actual cases, many people will encounter such a dilemma, so let the editor lead you to learn how to deal with these situations. I hope you can read it carefully and be able to achieve something!
Java generic implementation
Java implements generics by * * type erasure (Type erasure generics) * *. In vernacular, this generics exists only in the source code, and when the compiler compiles the source code into bytecode, the generics are "erased", so there are no generics in the bytecode.
For the following code, after compilation, we use javap-s class to look at the bytecode.
Method source code
Bytecode
Looking at the bytecode in the setParam section, you can see from descriptor that the generic T has been erased and finally replaced with Object.
"ps: not every generic parameter becomes an Object class when the type is erased. If the generic type is T extends String, it will eventually become String after the generic type is erased.
In the same way as the getParam method, the return value of the generic type is replaced with Object.
In order to ensure the correctness of the String param = genericType.getParam (); code, the compiler also has to insert type conversions here.
In addition, the compiler also defends against generic security, and if we add Integer to ArrayList, the program will report an error during compilation.
The code after the final type is erased is equivalent to the following:
Defects caused by type erasure
For comparison, let's briefly talk about the implementation of C # generics.
* * Cellular generics * generics are implemented as "Reifiable generics". Unfamiliar C# partners don't have to struggle with the concept of modernization technology, and I don't understand these features!
In a nutshell, generics implemented by * * Clippers * are real, whether in the program source code, after compilation, or even at run time.
Compared to C # generics, Java generics look like "pseudo" generics. Java generics only exist in the program source code and are erased after compilation. This defect will bring some problems accordingly.
Basic data types are not supported
After the generic parameter is erased, it is forced to be of type Object. This is not a problem for reference types, since Object is the parent of all types. But for the eight basic data types, such as int/long, this is difficult. Because Java can't do the forced conversion between int/long and Object.
If we want to realize this kind of transformation, we need to carry out a series of transformations, and it is difficult to change them. So at the time Java came up with a simple and crude solution: since there was no way to convert, primitive type generics were simply not supported.
If you need to use it, specify the generics of the relevant wrapper classes, such as ArrayList. In addition, the feature of automatic unboxing / boxing of native data types has been added for the convenience of developers.
It is this "lazy" practice that makes it impossible for us to use primitive type generics and has to endure the overhead of packing / unpacking packaging classes, which leads to the problem of running efficiency.
Operation efficiency
As we have seen in the bytecode example above, the type will become Object after generic erasure. When generics appear at the method input location, because Java can be transformed upwards, there is no need to cast, so there is no problem.
But when the generic parameter appears at the output position (return value) of the method, the place where the method is called needs to be converted downwards to cast the Object to the desired type, so the compiler inserts a sentence of checkcast bytecode.
In addition to this, we also mentioned the original basic data types above, and the compiler needs to help us with packing / unpacking.
So for the following code:
Listlist = new ArrayList (); list.add (66); / / 1 int num = list.get (0); / / 2
For ①, all the compiler has to do is add basic types of boxing. But for the second step, the compiler first needs to cast Object to Integer, and then the compiler needs to unbox.
After the type is erased, the above code is equivalent to:
List list = new ArrayList (); list.add (Integer.valueOf (66)); int num = ((Integer) list.get (0)). IntValue ()
If the above generic code had been implemented in C #, there wouldn't be so many extra steps. Therefore, the type erasure generics implementation of Java lags behind C # generics in terms of use effect and running efficiency.
The actual generic type cannot be obtained at run time
Because generics are erased after compilation, the Java virtual machine cannot get the actual types of generics while the code is running.
In the following code, the two List look like different types of collections from the source code, but after generic erasure, the collections become ArrayList. So the code in the if statement will be executed.
ArrayListli = new ArrayList (); ArrayListlf = new ArrayList (); if (li.getClass () = = lf.getClass ()) {/ / generic erase, two List types are the same System.out.println ("6666");}
This makes the code seem a little counterintuitive, which is not very friendly for beginners.
In addition, it will bring some limitations to our actual use, for example, we cannot directly implement the following code:
Finally, for example, if we need to implement a method that converts a generic List to an array, we cannot get the actual generic type directly from List, so we have to pass in an additional Class type to specify the type of the array:
Public staticE [] convert (Listlist, ClasscomponentType) {E [] array = (E []) Array.newInstance (componentType, list.size ());. }
As we can see from the above example, Java uses type erasure to implement generics, which has many defects. So why doesn't Java use the generic implementation of C #? Or adopt a better way to implement it?
When we understand the history of Java generics mechanism and the current situation of Java language at that time, we can first-hand understand the reason why Java adopted this way of implementing generics at that time.
Historical background of Java generics
Java generics were first introduced in JDK5, but the idea of generics first came from C++ templates (template). In 1996, Martin Odersky (the founder of the Scala language) extended the functions of generics and functional programming on the basis of the newly released Java to form a new language-"Pizza".
Later, the core Java development team was deeply interested in Pizza's generic design and worked with Martin to develop a new project, "Generic Java." The purpose of this project is to add generic support to Java, but does not introduce features such as functional programming. Finally, generics support was formally introduced into Java5.
The process of generic migration is not toward type erasure at first. In fact, generics in Pizza are more similar to generics in C #.
However, because of Java's own characteristics and strict constraints, Martin has to give up the generic design in Pizza in the process of Generic Java development.
This feature is that Java requires strict backward compatibility. In other words, a Class file compiled in JDK1.2 can not only run normally in JDK1.2, but also must be guaranteed to run normally in subsequent JDK, such as JDK12.
This feature is explicitly written into the Java language specification, which is a serious commitment to Java users.
"to emphasize here, backward compatibility refers to binary compatibility, not source compatibility. There is no guarantee that a higher version of Class files will run on an earlier version of JDK.
The difficulty now is that generics are not supported before Java 1.4.2, but Java5 suddenly has to support generics and programs compiled before JDK1.4 can run properly in the new version, which means that restrictions that did not exist before cannot be suddenly added.
For example:
ArrayList arrayList=new ArrayList (); arrayList.add ("6666"); arrayList.add (Integer.valueOf)
Before generics, List collections can store different types of data, so after the introduction of generics, this code must run correctly.
In order to ensure that these old Clas files work properly after Java5, designers basically have two ways:
Generic containers (mainly container types) are required, and some of the previous ones remain the same, adding a new set of generic versions in parallel.
Generalize existing types directly without adding any new generic versions of existing types.
If Java were implemented in the first way, we might now have two sets of collection types. Take ArrayList as an example, one is a common java.util.ArrayList, the other may be java.util.generic.ArrayList.
With this approach, if you need to use generic features in your development, use the new types directly. In addition, the old code can be run directly in the new version of JDK without any changes.
There seems to be no problem with this plan, but in fact, C# adopts it. But why didn't Java use this solution?
This is because C# was only released two years ago, there is not much historical code, and if the old code needs to use generic features, it can be modified very quickly. But Java is different. It has been ten years since Java was released, and many programs have been running and deployed in production environments. You can imagine that there is a lot of historical code.
If these applications need to use generics in the new version of Java, then you need to make a lot of source code changes, you can imagine the development effort.
In addition, before Java 5, we already had two sets of collection containers, one for Vector/Hashtable and other containers, and the other for ArrayList/ HashMap. The existence of these two sets of containers has actually caused some inconvenience, and for developers who are new to Java, they have to learn the difference between the two.
If new types are introduced for generics at this time, there will be four sets of containers coexisting at the same time. Think about this picture, a new contact developer, in the face of four sets of containers, do not know how to choose. How Java is really realized, there must be more people complaining about Java.
So Java chooses the second path, using type erasure, only needs to change the Javac compiler, does not need to change the bytecode, does not need to change the virtual machine, and ensures that the code that has no generics before can run in the new JDK.
But the second way, does not necessarily need to use type erasure implementation, if there is enough time to design well, there may be a better solution.
The technology debt left at that time can only be repaid by the Valhalla project. The project, which began in 2014, was originally planned to address defects in existing languages in JDK10. But as a result, we also know that now that we are all JDK14, only a small part of the goals have been achieved, but the core goals have not been solved, so we can see the difficulty of this change.
This is the end of the content of "what is the way to implement Java generics". Thank you for reading. If you want to know more about the industry, you can follow the website, the editor will output more high-quality practical articles for you!
Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.
Views: 0
*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.