Download presentation
Presentation is loading. Please wait.
Published byAnderson Pinhal Modified over 5 years ago
1
Introduction To Generics CSC 300 Fall 2019 Howard Rosenthal
LESSON 6 Introduction To Generics CSC 300 Fall 2019 Howard Rosenthal
2
Course References Materials for this course have utilized materials in the following documents. Additional materials taken from web sites will be referenced when utilized. Anderson, Julie and Franceschi, Herve, Java Illuminated 5TH Edition,, Jones and Bartlett, 2019 Bravaco, Ralph and Simonson, Shai, Java Programming From The Ground Up, McGraw Hill, 2010 Deitel, Paul and Deitel, Harvey, Java, How To Program, Early Objects, Eleventh Edition, Pearson Publishing, 2018 Gaddis, Tony, Starting Out With Objects From Control Structures Through Objects, Seventh Edition, Pearson Publishing, 2019 Horstmann, Cay, Core Java For The Impatient, Addison Wesley- Pearson Education, 2015 Naftalin, Maurice and Wadler, Philip, Java Generics and Collections, O’Reilly Media, 2007 Schmuller, Joseph, Teach Yourself UML In 24 Hours Second Edition, SAMS Publishing, 2002 Urma, Raoul-Gabriel, Fusco, Mario and Mycroft, Alan, Java 8 in Action: Lambdas, Streams, and Functional-Style Programming, Manning Publishing, 2014 Wirfs-Brock, Rebecca, Wilkerson, Brian and Wiener, Laura, Designing Object-Oriented Software, Prentice Hall, 1990 Oracle On-Line Documentation and Tutorials,
3
Lesson Goals Introduce the Collection Interface and Collections
Present some basics of generics Describe inference in Java and with Generics Define and show the use of target types
4
Brief Discussion On Collection Interface and Collections
5
The Collections Framework
The following discussion provides a brief introduction to the concept of a Collection. We will discuss in detail later this semester. The Collections Framework is a unified architecture for representing and manipulating collections, enabling them to be manipulated independently of the details of their representation. It contains prebuilt generic data structures It reduces programming effort while increasing performance. It enables interoperability among unrelated APIs, reduces effort in designing and learning new APIs, and fosters software reuse. It allows interoperability between APIs The framework is based on more than a dozen collection interfaces. It includes implementations of these interfaces and algorithms to manipulate them. The interface Collection contains bulk operations for adding, clearing and comparing objects in a collection. This includes adding a list of elements, or even a whole Collection, to a Collection Don’t confuse the Collection interface with the Collections class Helpful reference For reading Oracle Java documentation
6
Describing The Collections Class And Collections Objects
The Collections class consists exclusively of static methods that operate on or return collections. It contains polymorphic algorithms that operate on collections, "wrappers", which return a new collection backed by a specified collection, and a few other odds and ends. The methods of this class all throw a NullPointerException if the collections or class objects provided to them are null. The "destructive" algorithms contained in this class, that is, the algorithms that modify the collection on which they operate, are specified to throw UnsupportedOperationException if the collection does not support the appropriate mutation primitive(s), such as the set method. A collection is a data structure – actually an object – that can hold references to other objects Usually collections contain references to objects of any type that has an is-a relationship In other words it takes advantage of polymorphism – i.e. an ArrayList of a certain type can have as member any subclass of that type Collections cannot manipulate variables of primitive data This is why we use wrapper classes for the primitive variables, taking advantage of autoboxing and unboxing, when needing to use a Collection class (i.e. ArrayList) with objects Collection classes are typically implemented using generics See
7
The Java Collection Framework Hierarchy For The Collection interface
8
Collections class Implements A Number Of Useful Static Methods
Description sort Sorts the elements of a Collection in an input list See CollectionSorting.java or CollectionSorting1.java or (Time.java, TimeComparator.java and SortingTime.java) reverse Reverses all the elements in a Collection See CollectionSorting1.java shuffle Random shuffling of the elements of a Collection (very good in card simulations) See ShuffleExample.java or (Card.java, DeckOfCards.java and CardShuffleTester.java fill Sets every element in the Collection to refer to the given argument (see AlgorithmsForCollections.java) copy Copies the references from one Collection into another List. The destination List must be at least as long as the source List; otherwise, an IndexOutOfBoundsException occurs. (see AlgorithmsForCollections.java) min Returns the “smallest” element in a Collection, based on the elements comparator (i.e must implement the Comparable interface) max Returns the “largest” element in a Collection, based on the element’s comparable or comparator method (see AlgorithmsForCollections.java) binarySearch Locate an item in a sorted Collection using binary search See BinarySearchTest.java addAll Appends all the elements in an array to a Collection. See AlgorithmsForMoreCollectionMethods.java frequency Calculates how many Collection elements are equal to the specified element. See AlgorithmsForMoreCollectionMethods.java disjoint Returns true if two Collections have no elements in common otherwise false. See AlgorithmsForMoreCollectionMethods.java
9
The asList Method For Arrays
Class Arrays provides static method asList to view an array (sometimes called the backing array) as a List collection. A List view allows you to manipulate the array as if it were a list. This is useful for adding the elements in an array to a collection and for sorting array elements. UsingToArray.java is used to create a LinkedList with a List view of an array, because we cannot pass the array to a LinkedList constructor. The asList(array) method of java.util.Arrays class is used to return a fixed-size List backed by the specified array. This method acts as bridge between array-based and collection-based APIs, in combination with Collection.toArray(). The returned list is serializable and implements RandomAccess. This method also provides a convenient way to create a fixed-size list initialized to contain several elements List<String> stooges = Arrays.asList("Larry", "Moe", "Curly"); Changes to the returned list "write through" to the array. Any modifications made through the List view change the array, and any modifications made to the array change the List view. The only operation permitted on the view returned by as List is set, which changes the value of the view and the backing array. Any other attempts to change the view (such, as adding or removing elements) result in an UnsupportedOperationException. See AsListDemo1.java Must import java.util.Arrays, and java.util.XXX where XXX is the Collection class or interface being used.
10
The toArray Method For Arrays (1)
toArray is a method of the Collection interface that converts the elements of any Collection into a fixed sized array. The example below uses Arrays method asList to view an array as a List and uses List method toArray to get an array from a LinkedList collection. The program calls method asList to create a List view of an array, which is used to initialize a LinkedList object, then add. a series of Strings to the LinkedList and calls method toArray to obtain an array containing references to the Strings. If you change the List here it doesn’t update the Array. See UsingToArray1.java and UsingToArray2.java
11
The toArray Method For Arrays (1)
toArray(T[] a) where T is a List or any child of List Return Value: An array containing the elements of the list Throws: ArrayStoreException - if the runtime type of the specified array is not a supertype of the runtime type of every element in this list NullPointerException - if the specified array is null
12
Generics Basics
13
What Are Generics? The English definition of generic simply means:
Relating to or shared by a whole group of similar things; not specific to any particular thing: Java generic methods and generic classes enable programmers to specify, with a single method declaration, a set of related methods, or with a single class declaration, a set of related types, respectively. Generics also provide compile-time type safety that allows programmers to catch invalid types at compile time. Generics add stability to your code by making more of your bugs detectable at compile time. Using Java’s generic concept, we might write a generic method for sorting an array of objects, then invoke the generic method with Integer arrays, Double arrays, String arrays and so on, to sort the array elements.
14
Generic Classes The concept of a data structure, such as a stack, can be understood independently of the element type it manipulates. Generic classes provide a means for describing the concept of a stack (or any other class) in a type-independent manner. These classes are known as parameterized classes or parameterized types because they accept one or more type parameters.
15
Why Use Generics? (1) In a nutshell, generics enable types (classes and interfaces) to be parameters when defining classes, interfaces and methods. Much like the more familiar formal parameters used in method declarations, type parameters provide a way for you to re-use the same code with different inputs. The difference is that the inputs to formal parameters are values, while the inputs to type parameters are types. Code that uses generics has many benefits over non-generic code: Stronger type checks at compile time. A Java compiler applies strong type checking to generic code and issues errors if the code violates type safety. Fixing compile-time errors is easier than fixing runtime errors, which can be difficult to find. Elimination of casting in many cases.
16
Why Use Generics? (2) The following code snippet without generics requires casting: List list = new ArrayList(); list.add("hello"); String s = (String) list.get(0); When re-written to use generics, the code does not require casting: List<String> list = new ArrayList<String>(); String s = list.get(0); // no cast Enabling programmers to implement generic algorithms. By using generics, programmers can implement generic algorithms that work on collections of different types, can be customized, and are type safe and easier to read. The Java language is designed to enforce type safety. This means that programs are prevented from accessing memory in inappropriate ways. ... Type safety means that a program cannot perform an operation on an object unless that operation is valid for that object. Type enforcement can be static, catching potential errors at compile time, or dynamic, associating type information with values at run-time and consulting them as needed to detect imminent errors, or a combination of both.
17
Generics Allow You To Reuse The Same Code With Different Types of Data
Say you wish to process lists of various types of Integers, Strings, and even others lists of lists of Strings. Without generics this is simple. You can represent all three by the same class, called List, which has elements of class Object: list of Integers List list of Strings List list of lists of Strings List In order to keep the language simple, you are forced to do some of the work yourself: You must keep track of the fact that you have a list of Integers (or Strings or lists of Strings), and when you extract an element from the list you must cast it from Object back to Integer (or String or List). In Java with generics you may distinguish different types of lists: list of integers List<Integer> list of strings. List<String> list of lists of strings. List<List<String>> Now the compiler keeps track of whether you have a list of integers (or Strings or lists of Strings), and no explicit cast back to Integer (or String or List<String>) is required.
18
Generic Types - An Example (1)
A generic type is a generic class or interface that is parameterized over types. The following Box class example demonstrates the concept. Non-generic Box class Since its methods accept or return an Object, you are free to pass in whatever you want, provided that it is not one of the primitive types. There is no way to verify, at compile time, how the class is used. One part of the code may place an Integer in the box and expect to get Integers out of it, while another part of the code may mistakenly pass in a String, resulting in a runtime error. public class Box { private Object object; public void set(Object object) this.object = object; } public Object get() return object;
19
Generic Types - An Example (2)
A generic class is defined with the following format: class name<T1, T2, ..., Tn> { /* ... */ } The type parameter section, delimited by angle (diamond) brackets (<>), follows the class name. It specifies the type parameters (also called type variables) T1, T2, ..., and Tn. To update the Box class to use generics, you create a generic type declaration by changing the code "public class Box" to "public class Box<T>". This introduces the type variable, T, that can be used anywhere inside the class. All occurrences of Object are replaced by T. A type variable can be any non-primitive type you specify: any class type, any interface type, any array type, or even another type variable. With this change, the Box class becomes: public class Box<T> { private T t; // T stands for "Type" public void set(T t) this.t = t; } public T get() return t; See Box.java
20
Type Parameter Naming Conventions
By convention, type parameter names are single, uppercase letters. The reason is that without this convention, it would be difficult to tell the difference between a type variable and an ordinary class or interface name. The most commonly used type parameter names are: E - Element (used extensively by the Java Collections Framework) K - Key N - Number T - Type V - Value S,U,V etc. - 2nd, 3rd, 4th types You'll see these names used throughout the remaining lessons and in the Oracle on-line documentation
21
Instantiating A Generic Type (1)
To reference the generic Box class from within your code, you must perform a generic type invocation, which replaces T with some concrete value, such as Integer: Box<Integer> integerBox; You can think of a generic type invocation as being similar to an ordinary method invocation, but instead of passing an argument to a method, you are passing a type argument — Integer in this case — to the Box class itself. Like any other variable declaration, this code does not actually create a new Box object. It simply declares that integerBox will hold a reference to a "Box of Integer", which is how Box<Integer> is read. An invocation of a generic type is generally known as a parameterized type. To instantiate this class, use the new keyword, as usual, but place <Integer> between the class name and the parenthesis: Box<Integer> integerBox = new Box<Integer>();
22
Instantiating A Generic Type (2)
You can use type inference You can replace the type arguments required to invoke the constructor of a generic class with an empty set of type arguments (<>) as long as the compiler can determine, or infer, the type arguments from the context. This pair of angle brackets, <>, is informally called the diamond. For example, you can create an instance of Box<Integer> with the following statement: Box<Integer> integerBox = new Box<>(); Note: Many developers use the terms "type parameter" and "type argument" interchangeably, but these terms are not the same. When coding, one provides type arguments in order to create a parameterized type. Therefore, the T in Foo<T> is a type parameter and the String in Foo<String> f is a type argument. This lesson observes this definition when using these terms
23
Multiple Type Parameters (1)
A generic class can have multiple type parameters. For example, the generic OrderedPair class, which implements the generic Pair interface: public interface Pair<K, V> { public K getKey(); public V getValue(); } public class OrderedPair<K, V> implements Pair<K, V> private K key; private V value; public OrderedPair(K key, V value) this.key = key; this.value = value; public K getKey() return key; public V getValue() return value;
24
Multiple Type Parameters (2)
The following statements create two instantiations of the OrderedPair class: Pair<String, Integer> p1 = new OrderedPair<String, Integer>("Even", 8); Pair<String, String> p2 = new OrderedPair<String, String>("hello", "world"); The code, new OrderedPair<String, Integer>, instantiates K as a String and V as an Integer. Therefore, the parameter types of OrderedPair's constructor are String and Integer, respectively. Due to autoboxing, it is valid to pass a String and an int to the class. Because a Java compiler can infer the K and V types from the declaration OrderedPair<String, Integer>, these statements can be shortened using diamond notation: OrderedPair<String, Integer> p1 = new OrderedPair<>("Even", 8); OrderedPair<String, String> p2 = new OrderedPair<>("hello", "world"); To create a generic interface, follow the same conventions as for creating a generic class. You can also substitute a type parameter (i.e., K or V) with a parameterized type (i.e., List<String>). For example, using the OrderedPair<K, V> example: OrderedPair<String, Box<Integer>> p = new OrderedPair<>("primes", new Box<Integer>(...));
25
What Are Raw Data Types (1)
A raw type is the name of a generic class or interface without any type arguments. For example, given the generic Box class: public class Box<T> { public void set(T t) { /* ... */ } // ... } To create a parameterized type of Box<T>, you supply an actual type argument for the formal type parameter T: Box<Integer> intBox = new Box<>(); If the actual type argument is omitted, you create a raw type of Box<T>: Box rawBox = new Box(); Therefore, Box is the raw type of the generic type Box<T>. However, a non-generic class or interface type is not a raw type.
26
What Are Raw Data Types (2)
When using raw types, you essentially get pre-generics behavior — a Box gives you Objects. For backward compatibility, assigning a parameterized type to its raw type is allowed: Box<String> stringBox = new Box<>(); Box rawBox = stringBox; // OK But if you assign a raw type to a parameterized type, you get a warning: Box rawBox = new Box(); // rawBox is a raw type of Box<T> Box<Integer> intBox = rawBox; // warning: unchecked conversion You also get a warning if you use a raw type to invoke generic methods defined in the corresponding generic type: Box rawBox = stringBox; rawBox.set(8); // warning: unchecked invocation to set(T) The warning shows that raw types bypass generic type checks, deferring the catch of unsafe code to runtime. Bottom line: Avoid using raw types. It is also possible to get unchecked error messages when using raw data types The term "unchecked" means that the compiler does not have enough type information to perform all type checks necessary to ensure type safety.
27
Creating And Using Generic Methods
Generic methods are methods that introduce their own type parameters. This is similar to declaring a generic type, but the type parameter's scope is limited to the method where it is declared. Static and non-static generic methods are allowed, as well as generic class constructors (remember that constructors are really methods without any return value). The syntax for a generic method includes a list of type parameters, inside angle brackets, which appears before the method's return type. For static generic methods, the type parameter section must appear before the method's return type. The Util class includes a generic method, compare, which compares two Pair objects: See Util.java, Pair.java, TestPair.java Note: The type has been explicitly provided, but generally, this can be left out and the compiler will infer the type that is needed: Pair<Integer, String> p1 = new Pair<>(1, "apple"); Pair<Integer, String> p2 = new Pair<>(2, "pear"); boolean same = Util.compare(p1, p2); We will discuss inference in detail shortly
28
Generic Methods: Implementation and Compile-Time Translation
If the operations performed by several overloaded methods are identical for each argument type, the overloaded methods can be more conveniently coded using a generic method. You can write a single generic method declaration that can be called with arguments of different types. Based on the types of the arguments passed to the generic method, the compiler handles each method call appropriately. All generic method declarations have a type-parameter section (i.e. < T >) delimited by angle brackets that precedes the method’s return type. Each type-parameter section contains one or more type parameters, separated by commas. A type parameter, also known as a type variable, is an identifier that specifies a generic type name. Can be used to declare the return type, parameter types and local variable types in a generic method, and act as placeholders for the types of the arguments passed to the generic method (actual type arguments). A generic method’s body is declared like that of any other method. Type parameters can represent only reference types—not primitive types.
29
Bounded Parameters There may be times when you want to restrict the types that can be used as type arguments in a parameterized type. For example, a method that operates on numbers might only want to accept instances of Number or its subclasses. This is what bounded type parameters are for. To declare a bounded type parameter: List the type parameter's name, followed by the extends keyword, followed by its upper bound, which in the example below is Number. Note that, in this context, extends is used in a general sense to mean either "extends" (as in classes) or "implements" (as in interfaces). public <U extends Number> void inspect(U u) See Box.java and TestBox.java, then BoxBounded.java and TestBoxBounded.java (to see an error). Then BoxBounded.java and TestBoxBoundedV2.java (note autoboxing) Note the difference between T and U Bounded type parameters can be used with classes and interfaces as well as methods.
30
Multiple Bounds Java Generics supports multiple bounds:
<T extends superClassName & Interface1 & Interface2 & …> We can’t have more than one class in multiple bounds. If one of the bounds is a class, it must be specified first. For example: Class A { /* ... */ } interface B { /* ... */ } interface C { /* ... */ } class D <T extends A & B & C> { /* ... */ } If bound A is not specified first, you get a compile-time error See MultiBound.java, MultiBoundTest.java, A.java, B.java
31
Generic Methods And Bounded Types
Bounded type parameters are key to the implementation of generic algorithms. The following method counts the number of elements in an array T[] that are greater than a specified element elem. public static <T> int countGreaterThan(T[] anArray, T elem) { int count = 0; for (T e : anArray) if (e > elem) // compiler error ++count; return count; } The implementation of the method is straightforward, but it does not compile because the greater than operator (>) applies only to primitive types such as short, int, double, long, float, byte, and char. You cannot use the > operator to compare objects. To fix the problem, use a type parameter bounded by the Comparable<T> interface, which means that the Type must implement Comparable in order for the code to work. Remember that compare to returns a negative, a 0 or a positive integer public interface Comparable<T> public int compareTo(T o); The resulting code will be: public static <T extends Comparable<T>> int countGreaterThan(T[] anArray, T elem) if (e.compareTo(elem) > 0)
32
Comparable In Generics
Generic method maximum determines and returns the largest of its three arguments of the same type. The relational operator > cannot be used with reference types, but it’s possible to compare two objects of the same class if that class implements the generic interface Comparable<T> (package java.lang). All the type-wrapper classes for primitive types implement this interface. Generic interfaces enable you to specify, with a single interface declaration, a set of related types. Comparable<T> objects have a compareTo method. The method must return 0 if the objects are equal, a negative integer if object1 is less than object2 or a positive integer if object1 is greater than object2. A benefit of implementing interface Comparable<T> is that Comparable<T> objects can be used with the sorting and searching methods of class Collections (package java.util). See UsingComparable.java
33
Overloading Generic Methods - Summary
A generic method may be overloaded like any other method. A class can provide two or more generic methods that specify the same method name but different method parameters. A generic method can also be overloaded by non-generic methods. When the compiler encounters a method call, it searches for the method declaration that best matches the method name and the argument types specified in the call—an error occurs if two or more overloaded methods both could be considered best matches.
34
Generics And Inheritance
We know that with the use of polymorphism it is possible to assign an object of one type to an object of another type provided that the types are compatible. For example, you can assign an Integer to an Object, since Object is one of Integer's supertypes: Object someObject = new Object(); Integer someInteger = new Integer(10); someObject = someInteger; // OK In object-oriented terminology, this is called an "is a" relationship. Since an Integer is a kind of Object, the assignment is allowed. But Integer is also a kind of Number, so the following code is valid as well: public void someMethod(Number n) { /* ... */ } someMethod(new Integer(10)); // OK someMethod(new Double(10.1)); // OK The same is also true with generics. You can perform a generic type invocation, passing Number as its type argument, and any subsequent invocation of add will be allowed if the argument is compatible with Number: Box<Number> box = new Box<Number>(); box.add(new Integer(10)); // OK box.add(new Double(10.1)); // OK Now consider the following method: public void boxTest(Box<Number> n) { /* ... */ } What type of argument does it accept? By looking at its signature, you can see that it accepts a single argument whose type is Box<Number>. Are you allowed to pass in Box<Integer> or Box<Double>, as you might expect? The answer is "no", because Box<Integer> and Box<Double> are not subtypes of Box<Number>. This is a common misunderstanding when it comes to programming with generics, but it is an important concept to learn. (see Figure On Next Page)
35
You Must Pass A Subclass Of a Type
36
What Is Subtyping Subtyping is a key feature of object-oriented languages such as Java. In Java, one type is a subtype of another if they are related by an extends or implements clause. Here are some examples: Integer is a subtype of Number Double is a subtype of Number ArrayList<E> is a subtype of List<E> List<E> is a subtype Collection<E> Collection<E> is a subtype of Iterable<E> Subtyping is transitive, meaning that if one type is a subtype of a second, and the second is a subtype of a third, then the first is a subtype of the third. From the last two lines in the preceding list, it follows that List<E> is a subtype of Iterable<E>. If one type is a subtype of another, we also say that the second is a supertype of the first.
37
Generic Classes And Subtypes (1)
You can subtype a generic class or interface by extending or implementing it. The relationship between the type parameters of one class or interface and the type parameters of another are determined by the extends and implements clauses. Using the Collections classes (discussed in subsequent lectures) as an example, ArrayList<E> implements List<E>, and List<E> extends Collection<E>. So ArrayList<String> is a subtype of List<String>, which is a subtype of Collection<String>. So long as you do not vary the type argument, the subtyping relationship is preserved between the types.
38
Generic Classes And Subtypes (2)
So what happens when we want to define our own list interface, PayloadList, that associates an optional value of generic type P with each element. Its declaration might look like: interface PayloadList<E,P> extends List<E> { void setPayload(int index, P val); ... } The following parameterizations of PayloadList are subtypes of List<String>: PayloadList<String, String> PayloadList<String, Integer> PayloadList<String, Exception>
39
Subtyping And the Substitution Principle (1)
The Substitution Principle tells us that wherever a value of one type is expected, one may provide a value of any subtype of that type Formally the Substitution Principle states: A variable of a given type may be assigned a value of any subtype of that type, and a method with a parameter of a given type may be invoked with an argument of any subtype of that type. Consider the interface Collection<E>. One of its methods is add, which takes a parameter of type E: interface Collection<E> { public boolean add(E elt); ... } According to the Substitution Principle, if we have a collection of numbers, we may add an integer or a double to it, because Integer and Double are subtypes of Number. List<Number> nums = new ArrayList<Number>(); nums.add(2); nums.add(3.14); assert nums.toString().equals("[2, 3.14]"); Here, subtyping is used in two ways for each method call. The first call is permitted because nums has type List<Number>, which is a subtype of Collection<Number>, and 2 has type Integer (thanks to boxing), which is a subtype of Number. The second call is similarly permitted. In both calls, the E in List<E> is taken to be Number
40
Subtyping And the Substitution Principle (2)
It may seem reasonable that since Integer is a subtype of Number, it follows that List<Integer> is a subtype of List<Number>. But as we have discussed, this is not the case, because the Substitution Principle would rapidly get us into trouble. It is not always safe to assign a value of type List<Integer> to a variable of type List<Number>. Consider the following code fragment: List<Integer> ints = new ArrayList<Integer>(); ints.add(1); ints.add(2); List<Number> nums = ints; // compile-time error nums.add(3.14); This code assigns variable ints to refer to a list of integers, and then assigns nums to refer t0 the same list of integers; hence the call in the fifth line adds a double to this list, as shown in the last line. This must not be allowed! The problem is prevented by observing that here the Substitution Principle does not apply: The assignment on the fourth line is not allowed because List<Integer> is not a subtype of List<Number>, and the compiler reports that the fourth line is in error. What about the reverse? Can we take List<Number> to be a subtype of List<Integer>? No, that doesn’t work either, as shown by the following code: List<Number> nums = new ArrayList<Number>(); nums.add(2.78); nums.add(3.14); List<Integer> ints = nums; // compile-time error
41
Subtyping And the Substitution Principle (3)
The problem is prevented by observing that here the Substitution Principle does not apply because List<Number> is not a subtype of List<Integer>, and the compiler reports that the fourth line is in error. So List<Integer> is not a subtype of List<Number>, nor is List<Number> a subtype of List<Integer>; All we have is the trivial case, where List<Integer> is a subtype of itself, We also have that List<Integer> is a subtype of Collection<Integer>.
42
Inference
43
Inference In Java Type inference is a Java compiler's ability to look at each method invocation and corresponding declaration to determine the type argument (or arguments) that make the invocation applicable. The inference algorithm determines the types of the arguments and, if available, the type that the result is being assigned, or returned. Finally, the inference algorithm tries to find the most specific type that works with all of the arguments. In the following example, inference determines that the second argument being passed to the pick method is of type Serializable: static <T> T pick(T a1, T a2) { return a2; } Serializable s = pick("d", new ArrayList<String>());
44
Type Inference In Generic Methods
The following discussion uses Box.java, BoxDemo.java, and BoxDemoTest.java Generic methods showed a type inference which enables you to invoke a generic method as you would an ordinary method, without specifying a type between angle brackets The generic method addBox defines one type parameter named U. A Java compiler can usually infer the type parameters of a generic method call. In most cases, you do not have to specify them. For example, to invoke the generic method addBox, you can specify the type parameter with a type witness as follows: BoxDemo.<Integer>addBox(Integer.valueOf(10), listOfIntegerBoxes); Alternatively, if you omit the type witness, a Java compiler automatically infers (from the method's arguments) that the type parameter is Integer: BoxDemo.addBox(Integer.valueOf(20), listOfIntegerBoxes);
45
Type Inference When Instantiating A Generic Class
You can replace the type arguments required to invoke the constructor of a generic class with an empty set of type parameters (<>) as long as the compiler can infer the type arguments from the context. Remember, this pair of angle brackets is informally called the diamond. For example, consider the following variable declaration: Map<String, List<String>> myMap = new HashMap<String, List<String>>(); You can substitute the parameterized type of the constructor with an empty set of type parameters (<>): Map<String, List<String>> myMap = new HashMap<>(); Note that to take advantage of type inference during generic class instantiation, you must use the diamond. In the following example, the compiler generates an unchecked conversion warning because the HashMap() constructor refers to the HashMap raw type, not the Map<String, List<String>> type: Map<String, List<String>> myMap = new HashMap(); // unchecked conversion warning
46
Type Inference With Generic Constructors
Constructors can be generic (in other words, declare their own formal type parameters) in both generic and non-generic classes. Consider the following example: class MyClass<X> { <T> MyClass(T t) // ... } Consider the following instantiation of the class MyClass: new MyClass<Integer>("") This statement creates an instance of the parameterized type MyClass<Integer> The statement explicitly specifies the type Integer for the formal type parameter, X, of the generic class MyClass<X>. Note that the constructor for this generic class contains a formal type parameter, T. The compiler infers the type String for the formal type parameter, T, of the constructor of this generic class (because the actual parameter of this constructor is a String object). Compilers in Java SE 7 and later can infer the actual type parameters of the generic class being instantiated if you use the diamond (<>). Consider the following example: MyClass<Integer> myObject = new MyClass<>(""); In this example, the compiler infers the type Integer for the formal type parameter, X, of the generic class MyClass<X>. It infers the type String for the formal type parameter, T, of the constructor of this generic class.
47
Target Types The Java compiler takes advantage of target typing to infer the type parameters of a generic method invocation. The target type of an expression is the data type that the Java compiler expects depending on where the expression appears. Consider the method Collections.emptyList, which is declared as follows, followed by the assignment statement. static <T> List<T> emptyList(); List<String> listOne = Collections.emptyList(); This statement is expecting an instance of List<String> so this data type is the target type. Because the method emptyList returns a value of type List<T>, the compiler infers that the type argument T must be the value String. Alternatively, you could use a type witness and specify the value of T as follows, although this is not necessary: List<String> listOne = Collections.<String>emptyList(); However, this is not necessary in this context. Consider the following method: void processStringList(List<String> stringList) { // process stringList } In Java SE 8, the following statement compiles: processStringList(Collections.emptyList()); The notion of what is a target type has been expanded to include method arguments, such as the argument to the method processStringList. In this case, processStringList requires an argument of type List<String>. The method Collections.emptyList returns a value of List<T>, so using the target type of List<String>, the compiler infers that the type argument T has a value of String.
Similar presentations
© 2025 SlidePlayer.com Inc.
All rights reserved.