The labs on this site need JavaScript to run. The same content is written out below.
Fundamentals › Java
What does the <String> in List<String> do?
The <String> in List<String> is a marker that tells the compiler, "this list holds only String." With this marker, the compiler blocks any line that tries to add something that is not a String, and it also knows that what you take out of the list is a String, so it lets you store it straight into a String variable. Without the marker, the list accepts any object, and the compiler does not know what you take out. So you must write (String) yourself every time you take something out, and a wrong item is not found when you compile but only at run time, at the moment you take it out. In this lab you change <String>, the value you add, and (String), then compile and run the same code, and you see what the compiler reports and what is printed.
Checked against real execution · Eight combinations were compiled with javax.tools on JDK 21, run, and checked
import java.util.ArrayList;
import java.util.List;
public class Main {
public static void main(String[] args) {
// <String>: a marker that this list holds only String
List<String> names = new ArrayList<>();
names.add("Bob");
String first = names.get(0);
System.out.println(first.length());
}
}
The compiler and runtime are JDK 21.0.12.1 (javac 21), and compilation used the default options, with no warning options turned on. For all eight combinations, the source that is the same as the code on the screen (identical text line by line, including the import lines) was compiled with javax.tools, and for those that compiled, the class file was loaded with a new class loader and Main.main was run, and the results were checked against the standard output and exceptions that were captured. What was checked is whether compilation succeeded, the diagnostic code and English message of the first error (the "int cannot be converted to String" and "Object cannot be converted to String" of compiler.err.prob.found.req were restated as the two error descriptions on the screen), the number of errors and the line where each error occurred, whether the Main class file was created, the notes the compiler left other than errors, the output when run, and the kind, message, and line of the exception at run time. The facts that the step sentences rely on were also run separately and checked. A line that adds 123 to a list without <String> passes without an exception even at run time, 1 item is held in the list, and ClassCastException occurred only on the (String) line that takes it out (message: Integer cannot be cast to String). Even without <String>, a line that stores what you take out in an Object variable compiles, and a line that calls length() on what you take out without a cast does not compile. When <String> is present, you can call length() directly on what you take out. With the default options, the combinations that contain a raw type (both those that compiled and those that had an error) left not an error but two lines of notes (that an unsafe operation is used, and that you should recompile with -Xlint:unchecked), and they did not appear as warnings. When compiled separately with the warning options -Xlint:unchecked and -Xlint:rawtypes turned on, a raw type warning occurred on the declaration line that uses List and ArrayList without <>, an unchecked call warning occurred on the names.add line, and there was no warning on the line that takes out with (String). The combination with <String> and also (String) compiled with the default options without even a note, and with -Xlint:all turned on, a warning about an unnecessary cast occurred. The error of adding 123 to a list with <String> was reported by javac in a shortened form (note: Some messages have been simplified), and when recompiled with -Xdiags:verbose, it was a compiler.err.cant.apply.symbols error saying that no method matches add(int). The documentation was opened with WebFetch and checked. I followed the Java Language Specification (Java SE 21), Section 4.5 and Section 4.5.1 (a generic class with type arguments attached, such as List<String>, is a parameterized type, and a type argument must be a reference type, so List<int> cannot be used), Section 4.8 (a name without <> is a raw type, which is allowed only for compatibility with old code, and some accesses to the members of a raw type give an unchecked warning), Sections 5.5 and 5.1.6 (a conversion between reference types that needs a check examines the class of the object at run time and may throw ClassCastException if it is wrong), and the Java Tutorials "Why Use Generics?" (generics give stronger checks at compile time and remove the casts when taking out). The pages that were opened came back as summarized answers, so I could not read all of the original text directly, and jls-4.html was read only up to the first 100000 characters (Sections 4.5 and 4.8 were within that). That 123 is added to the list as an Integer object was not checked by opening Section 5.1.7 (boxing) of the specification; it was observed in the exception message at run time. The kind of error, the number of errors, the line where it occurs, and whether a note is left are not defined by the specification; they were observed on this javac. The code on the screen has the form of one class with only main. There are ways to get around the compiler's check even with <String>, and this lab did not cover writing your own generic classes or methods, wildcards, or the fact that <String> does not remain at run time (type erasure). They are covered in the in-depth lab "Generics".
Previous lab: Exceptions · Next lab: List and Map
Struck-through text was removed from the first version of the code; highlighted text was added.