How does understanding Java’s support for generics help you develop more robust code
Java 5 brought generics to the Java language. In this article, I introduce you to generics and discuss generic types, generic methods, generics and type inference, generic controversies, and generic and heap pollution.
What is a generic?
Generics are collections of related language features that allow types or methods to operate on objects of various types while providing compile-time type safety. Generic features solves the Java. Lang. ClassCastException thrown at run time s, this is unsafe because the code type (i.e., converts the object from the current type incompatible types).
Generics and the Java Collections framework
Generics are widely used in the Java Collections Framework (officially introduced in a future Java 101 article), but they are not unique to it. Generics can also be used to other parts of the Java standard library, including Java. Lang. Class, java.lang.Com parable, Java. Lang. ThreadLocal, and Java. Lang. Ref. WeakReference.
Consider the following code snippet, which illustrates the lack of type safety (in the context of classes in the Java Collections Framework) that was common in Java code before generics were introduced in Java.util. LinkedList:
List doubleList = new LinkedList();
doubleList.add(new Double(3.5));
Double d = (Double) doubleList.iterator().next();
Copy the code
Although the goal of the above program is to store only objects in the java.lang.Double list, there is nothing to prevent the storage of other types of objects. For example, you can specify doublelist.add (“Hello”); Add a java.lang.string object. However, when storing another kind of object, the (Double) cast operator ClassCastException on the last line will result in a throw when non-double objects are encountered.
Because this lack of type safety is not detected until run time, the developer may not be aware of the problem, leaving it to the client (rather than the compiler) to discover. The generic Double allows developers to mark lists as containing only Double objects, thereby helping the compiler to alert developers to the problem of storing objects with non-types in lists. This help is as follows:
List<Double> doubleList = new LinkedList<Double>();
doubleList.add(new Double(3.5)); Double d = doubleList. The iterator (). Next ();Copy the code
List is now “Double in List”. List is a generic interface, represented as List, that takes a Double argument, which is also specified when the actual object is created. The compiler can now enforce the correct type when adding objects to lists — for example, lists can only store Double values. This compulsion eliminates the need for a Double cast.
Discover generic types
GitHub star standard 115K + Java development tutorial, super hardcore!
A generic type is a formal type parameter list of a class or interface introduced by a set of parameterized types, which is a comma-separated list of parameter names of the types between diagonal supports. Generic types follow the following syntax:
Class identifier < formalTypeParameterList > {/ / the class body} Interface identifier < formalTypeParameterList > {/ / interface
}
Copy the code
The Java Collections Framework provides many examples of generic types and their argument lists (I’ve mentioned them throughout this article). For example, java.util.Set is a generic type and is a list of its formal type arguments, and E is a single type argument to the list. Another example is java.util.Map
.
Java type parameter naming convention
Java programming conventions dictate that type parameter names be single uppercase letters, such as Efor Element, Kfor key, Vfor Value, and Tfor Type. If possible, avoid meaningless names, such as p-java.util.List for List of elements, but you might mean List
What?
A parameterized type is the type parameter of a generic type with the actual type parameter (type name) that replaces the generic type instance. For example, Set is a parameterized type, where String is the actual type parameter E that replaces the type parameter.
The Java language supports actual type parameters for the following types:
- Concrete type: Passes the class or other reference type name to the type parameter. For example, in the List,Animal is passed to E.
- Specific parameterized types: Parameterized type names are passed to type parameters. For example, in Set
-
, the List is passed to E.
- Array type: Passes an array to a type parameter. For example, in Map
,>
, String is passed to K and passed to V by String[].
- Type parameters: Type parameters are passed to type parameters. For example, in class Container {Set Elements; },E is passed to E.
- Wildcard: question mark (?) Pass to the type parameter. For example, in Class
,? Is passed to T.
Each generic type means that there is a primitive type, which is a generic type with no formal type parameter list. For example, Class is the primitive type Class of. Unlike generic types, primitive types can be used on objects of any type.
Declare and use generic types in Java
Declaring a generic type involves specifying a list of formal type parameters and accessing those type parameters throughout the implementation. Using a generic type involves passing the actual type parameter to its type parameter when instantiating the generic type. See Listing 1.
Listing 1 :(gendemo.java version 1)
Class container <E> {private E[] element; Private integer index; Container (integer) {element = (E[]) new object [size]; Index =0;
}
void add(E)
{element [index ++] = element; } E get(intIndex) {return the element [index]; } integer size () {return index; }} open class GenDemo {public static void main(String[] args)
{container < string > con = new container < string >(5);
con.add("North");
con.add("South");
con.add("East");
con.add("The west");
for (int i = 0; i < con.size(); i++) System.out.println(con.get(i)); }}Copy the code
Listing 1 demonstrates generic type declaration and usage in the context of a simple container type that stores objects of appropriate parameter types. To keep the code simple, I omitted error checking.
The Container class declaration itself is specified as a generic form of a list of type parameters. The type parameter E identifies the type of the element to be stored, the element to be added to the internal array, and the type to return when retrieving the element.
The Container(int size) constructor is created by the array Elements = (E[]) new Object[size]; . If you’re wondering why I didn’t specify elements = new E[size]; Because it is impossible. Doing so may result in a ClassCastException.
Compile Listing 1 (javac gendemo.java). The (E[]) cast causes the compiler to output a warning that the actor is unchecked. It flags the possibility that converting E[] from Object[]to down may violate type safety, since Object[] can store objects of any type.
Note, however, that there is no way to violate type safety in this example. It is simply not possible for E to store non-objects in an internal array. Adding the prefix @suppressWarnings (” Unchecked “) to the Container(int Size) constructor suppresses this warning message.
Execute Java GenDemo to run the application. You should see the following output:
North south East westCopy the code
Boundary type parameters in Java
GitHub star standard 115K + Java development tutorial, super hardcore!
The E Set is an example of an unrestricted type parameter, because you can pass any actual type parameter E. For example, you can specify Set, Set, or Set.
Sometimes you want to restrict the types of actual type parameters that can be passed to type parameters. For example, you might want to limit the type parameter to accept only Employee and its subclasses.
You can limit type parameters by specifying an upper limit, which is the type as the upper limit of the type that can be passed as an actual type parameter. The upper limit is specified using the type name of the reserved word extends followed by the upper limit.
For example, class Employees limits the type of Employees that can be passed to Employee or a subclass (for example, Accountant). Specifying new Employees will be legal, while new Employees will be illegal.
You can assign multiple caps to a type parameter. However, the first boundary must always be the class, and the additional boundary must always be the interface. Each boundary is separated from its predecessor by a sum sign (&). Look at Listing 2.
Listing 2 :(gendemo.java version 2)
Import the Java. Math. BigDecimal; Import the Java. Util. Arrays; Abstract class employee {private BigDecimal hourlySalary; Private string name; Employee (string name, BigDecimal hourlySalary) {this.name = name;this.hourlySalary = hourlySalary; } publicBigDecimal getHourlySalary(a)
{return hourly wages; } public string getName() {return name; } Public string toString() {Return name +":"+ hourlySalary.toString(); }} Class Accountant extends Employee to achieve Comparable<Accountant> {Accountant (string name, BigDecimal hourlySalary) {Super (name, hourly rate); }public intCompareTo (accounting account) {return getHourlySalary().compareto (acct.gethourLysalary ()); }}class SortedEmployees<E extends Employee & Comparable<E>> {private E[] Employee; Private integer index; @SuppressWarnings("Not selected") SortedEmployees (integer size) {employee = (E[]) new employee [size]; Integer index =0; } invalid add (E emp) {employee [index ++] = emp; Arrays.sort(employees,0, index);
}
E get(intIndex)
{return employee [index]; } integer size () {return index; }} open class GenDemo {public static void main(String[] args)
{
SortedEmployees<Accountant> se = new SortedEmployees<Accountant>(10);
se.add(new Accountant("John Doe".new BigDecimal("35.40")));
se.add(new Accountant("George Smith".new BigDecimal("15.20")));
se.add(new Accountant("Jane Jones".new BigDecimal("25.60")));
for (int i = 0; i < se.size(); i++) System.out.println(se.get(i)); }}Copy the code
The Employee class in Listing 2 abstracts the concept of an hourly wage Employee. This class is a classed Accountant, which also implements Comparable to indicate that an Accountant can compare s in their natural order, which in this case happens to be the hourly wage.
The java.lang.Comparable interface is declared as a generic type T with a single type parameter named. This interface provides an int compareTo(T o) method that compares the current object to the argument (type T) and returns a negative integer, zero, or positive integer if the object is less than, equal to, or greater than the specified object.
This SortedEmployees class can store an instance of Employee implementation subclass Comparable in an internal array. After subclass instances are added, the array is sorted in ascending order by the hourly wage (via void Sort (Object[] a, int fromIndex, int toIndex) class method of java.util.Arrays) Employee.
Compile Listing 2 (javac gendemo.java) and run the application (Java GenDemo). You should see the following output:
George Smith:15.20Jane Jones:25.60John Doe:35.40
Copy the code
Lower bounds and generic type parameters
You cannot specify lower limits for generic type parameters
Consider wildcards
Suppose you want to print out a list of objects, whether they are strings, employees, shapes, or other types. Your first attempt might look something like listing 3.
Listing 3 :(gendemo.java version 3)
Import the Java. Util. ArrayList; Import the Java. Util. Iterator; Import the Java. Util. List; Open class GenDemo {public static void main(String[] args)
{List<String> Direction =newArrayList(); Direction. Add (" north "); Direction. Add (" south "); Direction. Add (" east "); Direction. Add (" west "); Print the list (direction); List<Integer> grades =new ArrayList();
Grades.add(new Integer(98));
Grades.add(new Integer(63));
Grades.add(new Integer(87)); Print a list (score); }static void printList(List<Object> list)
{
Iterator<Object> iter = list.iterator(); And (iter hasNext ()) System. Out. The println (iter) next ()); }}Copy the code
It seems logical that a list of strings or integers is a subtype of an object list, but the compiler complains when you try to compile the list. Specifically, it tells you that you cannot convert a list of strings into a list of objects, and the same is true for a list of integers.
The error message you receive has to do with the basic rules of generics:
For A given subtype of y of type X, and giving the raw type declaration (()), () is not ().
According to this rule, although String and java.lang.Integer are subtypes java.lang.Object of, they are not subtypes of List and List are subtypes of List.
Why do we have this rule? It’s helpful to remember that generics are designed to catch type-safety violations at compile time. Without generics, you’re more likely to be called to work at 2 a.m. because your Java program threw an aClassCastException and crashed!
As an example, let’s assume that List is a subtype List. If this is true, you might get the following code:
Direction = a List < String >newArrayList<String>(); List<Object> Object = direction; objects.add(newInteger()); String s = objects.get()0);
Copy the code
This code snippet creates a list of strings based on an arraylist. It then converts this list up to a list of objects (which is illegal, but just pretend it is for now). Next, it adds an integer to the list of objects, which violates type safety. The problem appears in the last line, which is thrown by ClassCastException because the stored integer cannot be converted to a string.
You can prevent this type safety violation by passing the List of type objects to the method in Listing 3 of printList(). However, this would not be very useful. Instead, you can use wildcards to solve the problem, as shown in Listing 4.
Listing 4 :(gendemo.java version 4)
Import the Java. Util. ArrayList; Import the Java. Util. Iterator; Import the Java. Util. List; Open class GenDemo {public static void main(String[] args)
{List<String> Direction =newArrayList<String>(); Direction. Add (" north "); Direction. Add (" south "); Direction. Add (" east "); Direction. Add (" west "); Print the list (direction); List<Integer> grades =new ArrayList<Integer>();
Grades.add(Integer.valueOf(98));
Grades.add(Integer.valueOf(63));
Grades.add(Integer.valueOf(87)); Print a list (score); }static void printList(List<? >list)
{iterator <? > iter =list.iterator(); And (iter hasNext ()) System. Out. The println (iter) next ()); }}Copy the code
In Listing 4, I use the wildcard (? Symbol) instead of the Object argument List and printList(). Since this symbol represents any type, it is legal to use the List and List methods.
Compile Listing 4 (javac gendemo.java) and run the application (Java GenDemo). The following output should be observed:
North south East west98
63
87
Copy the code
Discover generic methods
GitHub star standard 115K + Java development tutorial, super hardcore!
Now suppose you want to copy a list of objects into another list that is constrained by a filter. You might consider declaring a void copy(List SRC, List DST, Filter Filter) method, but this method can only copy the Objects on Lists and not anything else.
To pass a list of sources and targets of any type, you need to use wildcards as type placeholders. For example, consider the following copy() method:
void copy(List
src, List
dest, Filter filter)
{
for (int i = 0; i < src.size(); I++) if (filter. Receive (src.get(I))) dest. Add (src.get(I)); }Copy the code
The argument list for this method is correct, but there is a problem. According to the compiler, dest.add(src.get(I)); Type safety violation. This? Means that any type of object can be the element type of a list, and the source and target element types may not be compatible.
For example, if the source list is a ListofShape and the target list is a Listof String, and copy() allows continuation, then ClassCastException will be thrown when trying to retrieve elements of the target list.
You can partially solve this problem by providing upper and lower limits for wildcards, as shown below:
void copy(List<? extends String> src, List<? super String> dest, Filter filter)
{
for (int i = 0; i < src.size(); I++) if (filter. Receive (src.get(I))) dest. Add (src.get(I)); }Copy the code
You can provide an upper limit for wildcards by specifying extends followed by a type name. Similarly, you can provide a lower limit for wildcards by specifying super followed by a type name. These boundaries limit the types that can be passed as actual type parameters.
In this example, any actual type parameter of extends String can be interpreted as something that happens to be a String or a subclass. Similarly, you can put? Super String Any actual type parameter is interpreted to happen to be a String superclass or superclass. Because Stringis final, which means it can’t extend, it can only pass the list of the source of the Object String and the list of the target of the Object String or Object, which isn’t very useful.
You can completely solve this problem by using generic methods, which are class or instance methods that have type generalization implementations. Generic method declarations follow the following syntax:
< formalTypeParameterList > returnType identifier (parameterList)Copy the code
The list of formal type arguments for a generic method precedes its return type. It consists of type parameters and an optional upper bound. Type parameters can be used as return types and can appear in parameter lists.
Listing 5 demonstrates how to declare and call (call) a generic copy() method.
Listing 5 :(gendemo.java version 5)
Import the Java. Util. ArrayList; Import the Java. Util. List; Open class GenDemo {public static void main(String[] args)
{
List<Integer> grades = newArrayList<Integer>(); Integer [] Rank value = {integer.valueof (96),
Integer.valueOf(95),
Integer.valueOf(27),
Integer.valueOf(100),
Integer.valueOf(43),
Integer.valueOf(68)};for (int i = 0; i <gradeValues.length; i++)
Grades.add(gradeValues[i]);
List<Integer> failedGrades = newArrayList<Integer>(); Copy (score, failed score, new filter < integer > () {@override public Boolean accepted (integer level) {return grade.intValue() <=50; }});for (int i = 0; i < failedGrades.size(); i++)
System.out.println(failedGrades.get(i));
}
static <T> void copy(List
SRC, List
dest, Filter
)
{
for (int i = 0; i < src.size(); I++) if (filter. Receive (src.get(I))) dest. Add (src.get(I)); }} Interface filter <T> {Boolean accept (T o); }Copy the code
In Listing 5, I declare a void copy(List SRC, List dest, Filter Filter) generic method. The compiler notes that each of the type SRC, dest, and filter arguments includes a type parameter T. This means that the same actual type parameter must be passed during the method call, and the compiler can infer this parameter by examining the call.
If you compile Listing 5 (javac gendemo.java) and run the application (Java GenDemo), you should observe the following output:
27
43
Copy the code
Generics and type inference
GitHub star standard 115K + Java development tutorial, super hardcore!
The Java compiler includes a type inference algorithm that identifies the actual type parameters when instantiating a generic class, calling its generic constructor, or calling a generic method.
Generic class instantiation
Prior to Java SE 7, you had to specify the same actual type parameter for the generic type of a variable and the constructor when you instantiated a generic class. Consider the following example:
Map<String, Set<String>> Marble =new HashMap<String, Set<Integer>>();
Copy the code
Redundant actual type arguments in String, Set constructor calls confuse the source code. To help you eliminate this confusion, Java SE 7 has modified the type inference algorithm so that you can replace the actual type parameters of the constructor with an empty list (<>), provided that the compiler can infer the type parameters from the instantiation context.
Unofficially, <> is called the diamond operator, although it is not a real operator. Using the diamond operator produces the following more concise example:
Map<String, Set<String>> Marble =new HashMap<>();
Copy the code
To take advantage of type inference during generic class instantiation, you must specify the diamond operator. Consider the following example:
Map<String, Set<String>> Marble =new HashMap();
Copy the code
The compiler generates an “unchecked conversion warning” because the HashMap() constructor refers to the java.util.HashMap primitive type instead of the Map<String, Set> type.
Generic constructor calls
Generic and non-generic classes can declare generic constructors, where constructors have lists of formal type arguments. For example, you can use the generic constructor to declare the following generic classes:
Public class Box<E> {public <T> Box (T T) {// ...}}Copy the code
This declaration specifies the generic class E that Box has a formal type parameter. It also specifies the generic constructor T with formal type parameters. Consider the following example:
new Box<Marble>("Aggies")
Copy the code
This expression instantiates Box, passing Marble to e. In addition, the compiler inferences the actual type argument of StringasT, because the argument to the called constructor is a String.
We can further use the diamond operator to eliminate the actual type parameter in the Marble constructor call, as long as the compiler can infer the type parameter from the instantiation context:
Box<Marble> box = new Box<>("Aggies");
Copy the code
The compiler inferences the type Box of the Marble type parameter E of the generic class and the type T of the String type parameter T of the generic class’s constructor.
Generic method calls
When you call a generic method, you don’t have to provide the actual type parameter. Instead, the type inference algorithm checks the call and the corresponding method declaration to find the call’s type parameters. Inference algorithms identify the types of parameters and, if available, the types of results to assign or return.
The algorithm attempts to identify the most specific type that applies to all parameters. For example, in the following code snippet, type inference determines that the java.io.serializable interface new TreeSet() is of type Serializable for the second argument () passed to select() -Treesetimplements:
Serializable s = select("x".new TreeSet<String>());
static <T> T select(T a1, T a2)
{return a2; }Copy the code
I introduced static void copy(List SRC, List dest, Filter Filter), a generic class method that copies a List of sources to a List of targets, and is constrained by a Filter to determine which source objects to copy. Because of type inference, you can specify copy(/… /); Call this method. It is not necessary to specify the actual type parameters.
You may encounter situations where you need to specify the actual type parameter. For copy() or another class method, you can access the operator (.) in the class name and member. Then specify parameters like this:
GenDemo.<Integer>copy(grades, failedGrades, new Filter() / *... * /);
Copy the code
For instance methods, the syntax is almost the same. However, the actual type arguments will follow the constructor call and the member access operator, not the class name and operator:
new GenDemo().<Integer>copy(grades, failedGrades, new Filter() / *... * /);
Copy the code
Criticisms and limitations of generics in Java
While generics themselves may not be controversial, their specific implementation in the Java language has been. Generics are implemented as a compile-time feature that acts as syntactic sugar to eliminate casts. The compiler dismisses a list of formal type arguments for a generic type or a generic method after compiling the source code. This “dropping” behavior is called type erasure (or simply erasure). Other examples of erasure in generics include inserting casts into the appropriate type when the code type is incorrect, and replacing type parameters (such as Object) with their upper limits.
Erasing prevents generic types from being reified (exposing complete type information at run time). Therefore, the Java virtual machine cannot distinguish between, for example, a Set and a Set; At run time, only the primitive type Set is available. In contrast, primitive types, non-generic types (reference types prior to Java 5), primitive types, and wildcard calls are reified.
The inability to externalize generics leads to several limitations:
- With one exception, the instanceof operator cannot be used with parameterized types. An exception is an unbounded wildcard. For example, you cannot specify Set Shapes = null; if (shapes instanceof ArrayList) {}. Instead, you need to change the Instanceof expression to shapes Instanceof ArrayList
, which demonstrates an unbounded wildcard. Alternatively, you can specify shapes Instanceof ArrayList, which demonstrates primitive types (and is the preferred use). - Some developers point out that you cannot use Java reflection to get generic information that does not exist in the class file. However, in Java Reflection: Generics developer Jakob Jenkov pointed out some cases where generic information is stored in class files that can be accessed by Reflection.
- You cannot use type parameters in an array creation expression; Elements = new E[size]; . Generic Array Creation If you try this, the compiler reports an error message.
Given the limitations of erasure, you might wonder why you use erasure to implement generics. The reason is simple: The Java compiler is refactored to use erasures so that generic code can interoperate with non-generic legacy Java code (reference types cannot be parameterized). Without this backward compatibility, legacy Java code will not compile in a Java compiler that supports generics.
Generics and heap contamination
When using generics, you may encounter heap contamination where variables of a parameterized type refer to objects that do not belong to that parameterized type (for example, if the primitive type is mixed with the parameterized type). In this case, the compiler reports an “unchecked warning” because the correctness of an operation involving a parameterized type (such as a cast or method call) cannot be verified. Consider Listing 6.
Listing 6: Demonstrating heap contamination
Import the Java. Util. Iterator; Import the Java. Util. Set; Import the Java. Util. TreeSet; Public HeapPollutionDemo {public static void main(String[] args)
{
Set s = new TreeSet<Integer>();
Set<String> ss = s; // Warning not checked
s.add(Integer.valueOf(42)); // Another unchecked warningIterator<String> iter = ss.iterator(); While (it.hasnext ()) {String STR = it.next ();/ / throw ClassCastExceptionSystem.out.println(str); }}}Copy the code
The variable SS has a parameterized type Set. The compiler generates an unchecked warning when the Set is assigned to ss by the reference s. This is done because the compiler cannot be sure that s refers to the Set type (it does not). The result is heap contamination. The compiler allows this allocation to maintain backward compatibility with legacy Java versions that do not support generics. Also, erasing converts a Set to a Set, resulting in one Set being assigned to another.)
The compiler generates a second unchecked warning on the line that calls the add() method of the Set. This is done because it cannot determine whether the variable s refers to aSet or Set type. This is another case of heap contamination. (The compiler allows this method call because the erasure conversion Set’s Boolean add(E E) method is Boolean add(Object O), which can add objects of any type to the collection, including the Integer subtype Object.)
This video file cannot be played.(Error Code: 232001) Generic methods that include varargs arguments can also cause heap contamination. Listing 7 shows this scenario.
Listing 7: Demonstrating heap contamination in the context of an unsafe mutable parameter
Import the Java. Util. Arrays; Import the Java. Util. List; Public class UnsafeVarargsDemo {public static void main(String[] args)
{arrays.aslist (" A ", "B", "C"), arrays.aslist (" A ", "B", "C")"D"."E"."F")); } Static invalidation is not safe (list < string >... L) {Object[] oArray = l; oArray[0] = Arrays.asList(Double.valueOf(3.5)); String s = l[0].get(0); }}Copy the code
Object[] oArray = l; Assignment introduces the possibility of heap contamination. The variable parameter L of a parameter of type List whose parameterized type does not match the parameter type (String) can be assigned to the array variable oArray. However, the compiler does not generate unchecked warnings because it is converting the List… Lis List[] l. This assignment is valid because the variable L has type List[], which is a subtype Object[].
In addition, the compiler does not issue a warning or error when assigning an object of any type to any oArray array component; For example, oArray[0] = arrays.aslist (Double.valueof (3.5)); . This assignment is assigned to the first array component, Double, of the List object containing a single object, oArray.
String s = l[0].get(0); The distribution is problematic. The object L stored in the first array component of variable has a List of types, but this assignment requires a List of objects of type. As a result, the JVM throws a ClassCastException.
Compile the listing 7 source code (javac-xLint: Unchecked unsafevarargsdemo.java). When compiling under Java SE 12, you should observe the following output (slightly reformatted for readability) :
UnsafeVarargsDemo.java:8: Warning: [Unchecked] Generic Arrays create type lists for varargs arguments < string >[] is unsafe (Arrays.aslist (" A ", "B", "C"), ^ unsafevarargsdemos:12: Warning: [Unchecked] Possible heap contamination from a parameterized mutable parameter type list < string > is unsafe for static invalid (list < string >... L) ^2warningCopy the code
Earlier in this article, I said that you cannot use type parameters in an array creation expression. For example, you cannot specify elements = new E[size]; When you try to do this, the compiler reports a generic array creation error message. However, you can still create generic arrays, but only in a mutable parameter context, which is what the first warning message reports. Behind the scenes, the compiler converts the List… Lis List[] l, and then converted to List[] l.
Note that the heap contamination warning is generated at the declaration site of the unsafe() method. This message is not generated at the calling site for this method, as is the case for the Java 5 and Java 6 compilers.
Unsafe operation message migration
Prior to Java SE 7, every attempt to call a mutable parameter method with an uncrystallized mutable parameter type caused the compiler to print an “unsafe operation” warning at the point of call. To eliminate the possibility of many similar warning messages (one per calling site), Java SE 7 moved the warning from the calling site to the method declaration.
GitHub star standard 115K + Java development tutorial, super hardcore!
Not all variadic methods cause heap contamination. However, warning messages are still sent at the declaration site of the method. If you know that your method does not cause heap contamination, you can suppress this warning by declaring it with the @safevarargs annotation — Java SE 7 introduced the java.lang.SafeVarargs annotation type. For example, because the asList() method of the Arrays class has no way to pollute the heap, the declaration of this method is already annotated with @safevarargs, as follows:
@SafeVarargs
public static <T> List<T> asList(T... a)
Copy the code
The @Safevarargs annotation eliminates generic array creation and heap contamination warning messages. It is part of the document of the method contract and asserts that the implementation of the method does not improperly handle variadic form parameters.