Java 17 Recipes


Download 3.2 Mb.
Pdf ko'rish
bet138/245
Sana02.06.2024
Hajmi3.2 Mb.
#1839910
1   ...   134   135   136   137   138   139   140   141   ...   245
Bog'liq
Java 17 Recipes

 How It Works
The solution code demonstrates some basic use cases for generics. The examples in 
the GenericsDemo.java file, contained within the recipe sources, go into more detail to 
demonstrate the use of generics with Java collections versus showing you how to create 
generic types. Unless you develop a library API, you probably won’t be creating your own 
generic types. However, if you understand how generics are used with the Collection 
interfaces and classes, you can create your own generic types.
Chapter 7 Data SourCeS anD ColleCtionS


263
The first thing to understand and remember about Java generics is that they are 
strictly a compile-time feature that aids the developer in creating more type-safe code. 
All the type information that you specify when you parameterize a generic type gets 
“erased” by the compiler when the code is compiled down to byte code. You’ll see this 
described as type erasure. Let’s look at an example of a generic Collection type: the 
List. List is an interface defined as follows.
public interface List extends Collection { ... };
Now that is a strange syntax, especially because there is no object or type identified 
as E. As it turns out, the E is known as a type parameter, which is a placeholder to indicate 
to the compiler that a type is assigned to the object at runtime. Type parameters are 
typically uppercased letters that indicate the type of parameter being defined. There 
are a variety of different type parameters to note, but keep in mind that these are only 
applicable when defining a generic type. In most cases, generic types are only defined 
when developing a library or API.
• E – Element
• K – Key
N- Number
• T – Type
• V – Value
• S, U, V, and so on—second, third, and fourth types
To specify the element type for a List (or any Collection type), simply include the 
type name in angle brackets when declaring and instantiating objects. When you do this, 
you are specifying a parameterized type. The following code declares a list of integers. 
A variable, aList, of the parameterized type List is declared and then 
initialized with the reference obtained from the instantiation of the parameterized type, 
LinkedList (also called a concrete parameterized type).
List aList = new LinkedList();
Now that you’ve parameterized these types to restrict the element type to Integers, 
the List add(E e) method becomes
boolean add(Integer e);
Chapter 7 Data SourCeS anD ColleCtionS


264
If you try to add anything other than an integer to aList, the compiler generates 
an error.
aList.add(new Integer(121));
aList.add(42); // 42 is the same as new Integer(42), due to autoboxing.
aList.add("Java"); // won't compile, wrong type
It’s important to note that it’s the reference type checked at compile time, so the 
following also results in a compiler error.
Number aNum = new Integer("7");
aList.add(aNum); // won't compile, wrong type
This is a compile error because aNum could reference any Number object. If the 
compiler were to allow this, you could end up with a set containing doubles, floats, and 
so on, which would violate the Integer parameter constraint specified when you created 
aList. Of course, a simple type cast could get you around the compiler error, but this 
would surely cause unintended consequences when casting between incompatible 
Number objects. Generics were designed to reduce the amount of explicit type casting 
you must do in your code, so if you find yourself using explicit type casting when using 
methods of parameterized types, this is a clue of potentially dangerous code.
aList.add((Integer)aNum); // compiles, but don't do this.
Other things to watch out for when using generic types are compiler warnings. 
They may indicate that you’re doing something that is not recommended and it usually 
indicates that your code has a potential runtime error looming. An example can help to 
illustrate this. The following code compile but produce two compiler warnings.
List rawList = new LinkedList();
aList = rawList;
First, you’re creating rawList, which is a raw type, a generic type that isn’t 
parameterized. When generics were introduced into the language, the language 
designers decided that to maintain compatibility with pregenerics code, they would 
need to use raw types. However, the use of raw types is strongly discouraged for newer 
(post–Java 5) code, so compilers generate a raw type warning if you use them. Next, 
rawList is assigned to aList, which was created using parameterized types. Again, 
the compiler allows this (due to generics type erasure and backward compatibility). 
Chapter 7 Data SourCeS anD ColleCtionS


265
An unchecked conversion warning is generated for the assignment to flag potential 
runtime type incompatibility. Imagine if rawList contained strings. Later, if you tried 
to retrieve integer elements from aList, you would get a runtime error.
Regarding type compatibility, it doesn’t apply to generic type parameters. For 
example, the following is not a valid assignment.
List bList = new LinkedList(); // won't compile; 
incompatible types
Although integers are numbers (Integer is a subtype of Number), and LinkedList is 
a subtype of List, LinkedList is not a subtype of List. Fortunately, 
this won’t slip by you if you accidentally write code like this. The compiler generates an 
“incompatible types” warning.
So you may be wondering whether there is a way to achieve a variant subtyping 
relationship similar to what we tried to do in the previous line of code. The answer is yes
by using a generics feature called the wildcard. A wildcard is denoted by using a question 
mark (?) within the type parameter angle brackets. Wildcards declare parameterized 
types that are either bounded or unbounded. The following is an example declaration of 
a bounded parameterized type.
List cList;
An upper bound is established for the type parameter when a wildcard is used with 
the extends keyword. In this example, ? extends Number means any type that is either a 
Number or a subtype of a Number. Therefore, the following would be valid assignments 
because both Integer and Double are subtypes of Number.
cList = new LinkedList();
cList = new LinkedList();
cList = new LinkedList();
So, cList can hold a reference to any List instance with an element type compatible 
with Number. cList could even reference a raw type. This makes it a challenge for the 
compiler to enforce type safety to allow elements to be added to cList. Therefore, the 
compiler does not allow elements (other than a null) to be added to a collection type that 
is parameterized with ? extends. The following would result in a compiler error.
cList.add(new Integer(5)); // add() not allowed; cList could be 
LinkedList
Chapter 7 Data SourCeS anD ColleCtionS


266
However, you can get an element from the list without any problem.
Number cNum = cList.get(0);
The only restriction here is that the reference you get from the list must be treated 
like a number. Remember, cList could be pointing to a list of integers, a list of doubles, 
or list of any other subtype of Number.
A wildcard can also be used with the super keyword. In this case, a lower bound is 
established for the type parameter.
List dList;
In this example, ? super Integer means any type that is either Integer or any 
supertype of it. Therefore, the following would be valid assignments because Number and 
Object are the only supertypes of Integer.
dList = new LinkedList();
dList = new LinkedList();
dList = new LinkedList();
So, you see that Integer is the lower bound. This lower bound now restricts 
retrieving elements from the list. Because dList can reference any one of the previous 
parameterized types, the compiler would not be able to enforce type safety if an 
assumption were made about the type of the element being retrieved. Therefore, the 
compiler must not allow calls to get() on a collection type that is parameterized with ? 
super, and the following would result in a compiler error.
Integer n = dList.get(0); // get() not allowed; dList.get(0) could be a 
Number or Object
However, now you can add elements to the list, but the lower bound, Integer, 
still applies. Only Integers can be added because Integer is compatible with Number 
and Object.
dList.add(new Integer(5)); // OK
Number dNum = new Double(7);
dList.add(dNum); // won't compile; dList could be LinkedList
Chapter 7 Data SourCeS anD ColleCtionS


267
You see the use of the wildcard with both extends and super throughout the 
collection types. You often see them used in method parameter types, such as the 
addAll() method, which is defined for all collections. Sometimes you see the collection 
types using the wildcard (?) alone as a type parameter, which is called an unbounded 

Download 3.2 Mb.

Do'stlaringiz bilan baham:
1   ...   134   135   136   137   138   139   140   141   ...   245




Ma'lumotlar bazasi mualliflik huquqi bilan himoyalangan ©fayllar.org 2024
ma'muriyatiga murojaat qiling