|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
HOME
| COURSES
| TALKS
| ARTICLES
| GENERICS
| LAMBDAS
| IOSTREAMS
| ABOUT
| CONTACT
|
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Java Generics FAQs - Under The Hood Of The Compiler
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
This is a collection of answers to frequently asked questions
(FAQs) about Java Generics, a new language feature added to the Java programming
language in version 5.0 of the Java Standard Edition (J2SE 5.0).
If you want to provide feedback or have any questions regarding Java
generics, to which you cannot find an answer in this document, feel free
to send me
EMAIL
or use the
GENERICS FAQ
form.
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Technicalities - Under The Hood Of The Compiler© Copyright 2003-2022 by Angelika Langer. All Rights Reserved. |
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| A warning by which the compiler indicates that it cannot ensure type safety. | |
|
The term "unchecked" warning is misleading. It does
not mean that the warning is unchecked in any way. The term "unchecked"
refers to the fact that the compiler and the runtime system do not have
enough type information to perform all type checks that would be necessary
to ensure type safety. In this sense, certain operations are "unchecked".
The most common source of "unchecked" warnings is the use of raw types. "unchecked" warnings are issued when an object is accessed through a raw type variable, because the raw type does not provide enough type information to perform all necessary type checks. Example (of unchecked warning in conjunction with raw types): TreeSet se t = new TreeSet();When the add method is invoked the compiler does not know whether it is safe to add a String object to the collection. If the TreeSet is a collection that contains String s (or a supertype thereof), then it would be safe. But from the type information provided by the raw type TreeSet the compiler cannot tell. Hence the call is potentially unsafe and an "unchecked" warning is issued. "unchecked" warnings are also reported when the compiler finds a cast whose target type is either a parameterized type or a type parameter. Example (of an unchecked warning in conjunction with a cast to a parameterized type or type variable): A cast whose target type is either a (concrete or bounded wildcard) parameterized type or a type parameter is unsafe, if a dynamic type check at runtime is involved. At runtime, only the type erasure is available, not the exact static type that is visible in the source code. As a result, the runtime part of the cast is performed based on the type erasure, not on the exact static type. In the example, the cast to Wrapper<T> would check whether the object returned from super.clone is a Wrapper , not whether it is a wrapper with a particular type of members. Similarly, the casts to the type parameter T are cast to type Object at runtime, and probably optimized away altogether. Due to type erasure, the runtime system is unable to perform more useful type checks at runtime. In a way, the source code is misleading, because it suggests that a cast to the respective target type is performed, while in fact the dynamic part of the cast only checks against the type erasure of the target type. The "unchecked" warning is issued to draw the programmer's attention to this mismatch between the static and dynamic aspect of the cast. |
|
| LINK TO THIS | Technicalities.FAQ001 |
| REFERENCES |
What
does type-safety mean?
How can I disable or enable unchecked warnings? What is the raw type? Can I use a raw type like any other type? Can I cast to a parameterized type? Can I cast to the type that the type parameter stands for? |
How
can I disable or enable "unchecked" warnings?
| Via the compiler options -Xlint:unchecked and -Xlint:-unchecked and via the standard annotation @SuppressWarnings("unchecked") . | |
|
The compiler option
-Xlint:-unchecked
disables
all
unchecked warnings that would occur in a compilation.
The annotation @SuppressWarnings("unchecked") suppresses all warnings for the annotated part of the program. Note, in the first release of Java 5.0 the SuppressWarnings annotation is not yet supported. |
|
| LINK TO THIS | Technicalities.FAQ002 |
| REFERENCES |
What
is the -Xlint:unchecked compiler option?
What is the SuppressWarnings annotation? |
| The compiler option -Xlint:unchecked enables "unchecked" warnings, the option -Xlint:-unchecked disables all unchecked warnings. | |
"unchecked" warnings are by default disabled. If
you compile a program with no particular compiler options then the compiler
will not report any "unchecked" warnings. If the compiler finds source
code for which it would want to report an "unchecked" warning it only gives
a general hint. You will find the following note at the end of the
list of all other errors and warnings:
Note: util/Wrapper.java uses unchecked or unsafe operations.If you want to see the "unchecked" warnings you must start the compiler with the -Xlint:unchecked option. Example (of globally enabling unchecked warnings): javac -Xlint:unchecked util/Wrapper.javaThe option -Xlint:unchecked en ables the "unchecked" warnings. The "unchecked" warnings are also enabled when you use the -Xlint:all option. The option -Xlint:-unchecked disables the "unchecked" warnings. This is useful to suppress all "unchecked" warnings, while other types of warnings remain enabled. Example (of globally disabling unchecked warnings): javac -g -source 1.5 -Xlint:all -Xlint:-unchecked util/Wrapper.javaIn this example, using -Xlint:all all warnings (such as "unchecked", "deprecated", "fallthrough", etc.) are enabled and subsequently the "unchecked" warnings are disabled using -Xlint:-unchecked . As a result all warnings except "unchecked" warnings will be reported. |
|
| LINK TO THIS | Technicalities.FAQ003 |
| REFERENCES |
What
is an "unchecked" warning?
What is the SuppressWarnings annotation? |
| A standard annotation that suppresses warnings for the annotated part of the program. | |
|
The compiler supports a number of standard annotations
(see package
java.lang.annotation
).
Among them is the
SuppressWarnings
annotation. It contains a list
of warning labels. If a definition in the source code is annotated
with the
SuppressWarnings
annotation, then all warnings, whose
labels appear in the annotation's list of warning labels, are suppressed
for the annotated definition or any of its parts.
The SuppressWarnings annotation can be used to suppress any type of labelled warning. In particular we can use the annotation to suppress "unchecked" warnings. Example (of suppressing unchecked warnings): This example would usually raise 2 "unchecked" warnings. Since we annotated the entire class, all unchecked warnings raised anywhere in the class implementation are suppressed. We can suppress several types of annotations at a time. In this case we must specify a list of warning labels. Example (of suppressing several types of warnings): This example would usually raise 2 warnings when the call to method add is compiled: warning: [deprecation] Date(int,int,int) in java.util.Date has been deprecatedThe annotation preceding the enclosing method suppresses all unchecked and deprecation warnings anywhere in the method implementation. Annotations can not be attached to statements, expressions, or blocks, only to program entities with a definition like types, variables, etc. Example (of illegal placement of annotation): public static void someMethod() {Annotations can be attached to the definition of packages, classes, interfaces, fields, methods, parameters, constructors, local variables, enum types, enum constants, and annotation types. An annotated package declaration must go into a file named package-info.java in the directory that represents the package. Note, in release of Java 5.0 the SuppressWarnings annotation is not yet supported by all compilers. Sun's compiler will support in in release 6.0. |
|
| LINK TO THIS | Technicalities.FAQ004 |
| REFERENCES |
What
is an "unchecked" warning?
How can I disable or enable unchecked warnings? |
| By using an unbounded wildcard parmeterized type as target type of a cast expression. | |
|
Occasionally, we would like to cast to a parameterized
type, just to discover that the compiler flags it with an "unchecked" warning.
As we are interested in warning-free compilation of our source code, we
would like to avoid this warning. Use of an unbounded wildcard parameterized
type instead of a concrete or a bounded wildcard parameterized type would
help avoid the warning.
A typical example is the implementation of methods such as the equals method, that take Object reference and where a cast down to the actual type must be performed. Example (not recommended): class Wrapper<T> {When we replace the cast to Wrapper<T> by a cast to Wrapper<?> the warning disappears, because unbounded wildcard parameterized types are permitted as target type of a cast without any warnings. Example (implementation of equals ): class Wrapper<T> {Note, this technique works in this example only because we need no write access to the fields of the object refered to through the wildcard parameterized type and we need not invoke any methods. Remember, use of the object that a wildcard reference variable refers to is restricted. In other situations use of a wildcard parameterized type might not be a viable solution, because full access to the referenced object is needed. |
|
| LINK TO THIS | Technicalities.FAQ005 |
| REFERENCES |
Can
I cast to a parameterized type?
What is an "unchecked" warning? How can I disable or enable unchecked warnings? How do I best implement the equals method of a generic type? |
| Almost. | |
|
"Unchecked" warnings stem either from using generic types
in their raw form or from casts whose target type is a type parameter or
a concrete or bounded wildcard parameterized type. If you refrain from
both using raw types and the critical casts you can theoretically eliminate
all "unchecked" warnings. Whether this is doable in practice depends
on the circumstances.
Raw types. When source code is compiled for use in Java 5.0 that was developed before Java 5.0 and uses classes that are generic in Java 5.0, then "unchecked" warnings are inevitable. For instance, if "legacy" code uses types such as List , which used to be a regular (non-generic) types before Java 5.0, but are generic in Java 5.0, all these uses of List are considered uses of a raw type in Java 5.0. Use of the raw types will lead to "unchecked" warnings. If you want to eliminate the "unchecked" warnings you must re-engineer the "legacy" code and replace all raw uses of List with appropriate instantiations of List such as List<String> , List<Object> , List<?> , etc. All "unchecked" warnings can be eliminated this way. In source code developed for Java 5.0 you can prevent "unchecked" warnings in the first place by never using raw types. Always provide type arguments when you use a generic type. There are no situations in which you are forced to use a raw type. In case of doubt, when you feel you have no idea which type argument would be appropriate, try the unbounded wildcard " ? ". In essence, "unchecked" warnings due to use of raw types can be eliminated if you have access to legacy code and are willing to re-engineer it. Casts. "Unchecked" warnings as a result of cast expressions can be eliminated by eliminating the offensive casts. Eliminating such casts is almost always possible. There are, however, a few situations where a cast to a type parameter or a concrete or bounded wildcard parameterized type cannot be avoided. These are typically situations where a method returns a supertype reference to an object of a more specific type. The classic example is the clone method; it returns an Object reference to an object of the type on which it was invoked. In order to recover the returned object's actual type a cast in necessary. If the cloned object is of a parameterized type, then the target type of the cast is an instantiation of that parameterized type, and an "unchecked" warning is inevitable. The clone method is just one example that leads to unavoidable "unchecked" warnings. Invocation of methods via reflection has similar effects because the return value of a reflectively invoked method is returned via an Object reference. It is likely that you will find further examples of unavoidable "unchecked" casts in practice. For a detailed discussion of an example see Technicalities.FAQ502 , which explains the implementation of a clone method for a generic class.
In sum, there are situations in which you cannot eliminate "unchecked"
warnings due to a cast expression.
|
|
| LINK TO THIS | Technicalities.FAQ006 |
| REFERENCES |
What
is an "unchecked" warning?
How do I best implement the clone method of a generic type? |
| Because the compiler performs all type checks based on the type erasure when you use a raw type. | |
|
Usually the compiler issues an "unchecked" warning in order
to alert you to some type-safety hazard that the compiler cannot prevent
because there is not enough type information available. One of these situations
is the invocation of methods of a raw type.
Example (of unchecked warning in conjunction with raw types): class TreeSet<E> {When the add method is invoked the compiler does not know whether it is safe to add a String object to the collection because the raw type TreeSet does not provide any information regarding the type of the contained elements. Curiously, an unchecked warning is also issued in situations where there is enough type information available.
Example (of a spurious unchecked warning in conjunction with raw types):
class
SomeType<T> {
In this example, there is no type information missing. The
getList
method is declared to return a
List<String>
and this is so even in the raw type because the method does not depend
on the enclosing class's type parameter. Yet the compiler complains.
public List<String> getList() { ... } } SomeType raw = new SomeType (); List<String> listString = raw.getList(); // unchecked warning warning: [unchecked] unchecked conversion found : List required: List<String> List<String> listString = raw.getList(); ^ The reason is that the compiler computes the type erasure of a generic type when it finds an occurrence of the raw type in the source code. Type erasure does not only elide all occurances of the type parameter T , but also elides the type argument of the getList method's return type. After type erasure, the getList method returns just a List and no longer a List<String> . All subsequent type checks are performed based on the type erasure; hence the "unchecked" warning.
The "unchecked" warning can easily be avoided by refraining from use
of the raw type.
SomeType
is a generic type and should always be used with a type argument. In general,
the use of raw types will inevitably result in "unchecked" warnings; some
of the warnings may be spurious, but most of them are justified.
Note, that no spurious warning is issued when the method in question is a static method.
Example (of invoking a static method of a raw type):
class
SomeType<T> {
public static List<String> getList() { ... } } SomeType raw = new SomeType (); List<String> listString = raw.getList(); // fine |
|
| LINK TO THIS | Technicalities.FAQ007 |
| REFERENCES |
What
is an "unchecked" warning?
Should I prefer parameterized types over raw types? Why shouldn't I mix parameterized and raw types, if I feel like it? |
| A situation where a variable of a parameterized type refers to an object that is not of that parameterized type. | |
|
It can happen that a variable of a parameterized type such
as
List<String>
refers
to an object that is not of that parameterized type.
Example (of heap pollution):
List
ln = new ArrayList<Number>();
List<String> ls =ln; // unchecked warning String s = ls.get(0); // ClassCastException
|
|
| LINK TO THIS | Technicalities.FAQ050 |
| REFERENCES |
What
is an "unchecked" warning?
When does heap pollution occur? |
When
does heap pollution occur?
| As a result of mixing raw and parameterized type, unwise casting, and separate compilation. | |
Heap pollution occurs in three situations:
Raw Types Heap pollution can occur when raw types and parameterized types are mixed and a raw type variable is assigned to a parameterized type variable. Note, that heap pollution does not necessarily occur, even if the compiler issues an unchecked warning. Example (of mixing raw and parameterized types): List ln = new ArrayList<Number>(); Mixing raw and parameterized types should be avoided, if possible. It cannot be avoided when non-generic legacy code is combined with modern generic code. But otherwise, the mix is bad programming style.
Unchecked Casts Unwise casting can lead to all kinds of unhealthy situations. In particular, in can lead to heap pollution. Example (of cast to parameterized type polluting the heap): List<? extends Number> ln = new ArrayList<Long>(); Casts with a parameterized target type can lead to heap pollution, and so do casts to type variables.
Example (of cast to type variable polluting the heap):
<S,T> S convert(T arg) {
return (S)arg; // unchecked warning } Number n = convert(new Long(5L)); // fine String s = convert(new Long(5L)); // ClassCastException
Casts, whose target type is a parameterized type or a type variable, should be avoided, if possible.
Separate Compilation Another situation, in which heap pollution can occur is separate compilation of translation units.
Example (initial implementation):
file
FileCrawler.java
:
final class FileCrawler {
file
Test
.
java
:
final class Test {
Example (after modification and co-compilation):
file
FileCrawler.java
:
final class FileCrawler {
file
Test
.
java
:
final class Test { If we compiler separately, that is, only compile the modified file Test.java , then no error would be reported. This is because the class, in which the error occurs, has not been re-compiled. When the program is executed, a ClassCastException will occur.
Example (after modification and separate compilation):
file
FileCrawler.java
:
final class FileCrawler {
file
Test
.
java
:
final class Test { Separate compilation in general is hazardous, independently of generics. If you provide a method that first returns a String and later you change it to return a StringBuilder , without re-compiling all parts of the program that use the method, you end up in a similarly disastrous situation. The crux is the incompatible change of the modified method. Either you can make sure that the modified part is co-compiled with all parts that use it or you must not introduce any incompatible changes such as changes in semantics of types or signatures of methods. |
|
| LINK TO THIS | Technicalities.FAQ051 |
| REFERENCES |
What
is heap pollution?
What is an "unchecked" warning? How can I avoid "unchecked cast" warnings? Is it possible to eliminate all "unchecked" warnings? |
| By creating one unique byte code representation of each generic type (or method) and mapping all instantiations of the generic type (or method) to this unique representation. | |
|
The Java
compiler is responsible for translating Java source code that contains
definitions and usages of generic types and methods into Java byte code
that the virtual machine can interpret. How does that translation work?
A compiler that must translate a generic type or method (in any language, not just Java) has in principle two choices: Code sharing. The compiler generates code for only one representation of a generic type or method and maps all the instantiations of the generic type or method to the unique representation, performing type checks and type conversions where needed.
Code specialization is the approach that C++ takes for its templates:
Code specialization is particularly wasteful in cases where the elements in a collection are references (or pointers), because all references (or pointers) are of the same size and internally have the same representation. There is no need for generation of mostly identical code for a list of references to integers and a list of references to strings. Both lists could internally be represented by a list of references to any type of object. The compiler just has to add a couple of casts whenever these references are passed in and out of the generic type or method. Since in Java most types are reference types, it deems natural that Java chooses code sharing as its technique for translation of generic types and methods. The Java compiler applies the code sharing technique and creates one unique byte code representation of each generic type (or method). The various instantiations of the generic type (or method) are mapped onto this unique representation by a technique that is called type erasure . |
|
| LINK TO THIS | Technicalities.FAQ100 |
| REFERENCES | What is type erasure? |
| A process that maps a parameterized type (or method) to its unique byte code representation by eliding type parameters and arguments. | |
|
The compiler generates only one byte code representation
of a generic type or method and maps all the instantiations of the generic
type or method to the unique representation. This mapping is performed
by type erasure. The essence of type erasure is the removal of all
information that is related to type parameters and type arguments. In addition,
the compiler adds type checks and type conversions where needed and inserts
synthetic bridge methods if necessary. It is important to understand type
erasure because certain effects related to Java generics are difficult
to understand without a proper understanding of the translation process.
The type erasure process can be imagined as a translation from generic Java source code back into regular Java code. In reality the compiler is more efficient and translates directly to Java byte code. But the byte code created is equivalent to the non-generic Java code you will be seeing in the subsequent examples. The steps performed during type erasure include:
Eliding type parameters.
Eliding type arguments.
Example (before type erasure): interface Comparable <A> {Type parameters are green and type arguments are blue . During type erasure the type arguments are discarded and the type paramters are replaced by their leftmost bound. Example (after type erasure): interface Comparable {The generic Comparable interface is translated to a non-generic interface and the unbounded type parameter A is replaced by type Object . The NumericValue class implements the non-generic Comparable interface after type erasure, and the compiler adds a so-called bridge method . The bridge method is needed so that class NumericValue remains a class that implements the Comparable interface after type erasure. The generic method max is translated to a non-generic method and the bounded type parameter A is replaced by its leftmost bound, namely Comparable . The parameterized interface Iterator<A> is translated to the raw type Iterator and the compiler adds a cast whenever an element is retrieved from the raw type Iterator .
The uses of the parameterized type
LinkedList<NumericValue>
and the generic
max
method in the
main
method are translated
to uses of the non-generic type and method and, again, the compiler must
add a cast.
|
|
| LINK TO THIS | Technicalities.FAQ101 |
| REFERENCES |
What
is a bridge method?
Why does the compiler add casts when it translates generics? How does type erasure work when a type parameter has several bounds? |
| Representing type parameters and arguments of generic types and methods at runtime. Reification is the opposite of type erasure . | |
|
In Java, type parameters and type arguments are elided
when the compiler performs type erasure. A side effect of type erasure
is that the virtual machine has no information regarding type parameters
and type arguments. The JVM cannot tell the difference between a
List<String>
and a
List<Date>
.
In other languages, like for instance C#, type parameters and type arguments of generics types and methods do have a runtime representation. This representation allows the runtime system to perform certain checks and operations based on type arguments. In such a language the runtime system can tell the difference between a List<String> and a List<Date> . |
|
| LINK TO THIS | Technicalities.FAQ101A |
| REFERENCES |
What
is type erasure?
What is a reifiable type? |
| A synthetic method that the compiler generates in the course of type erasure. It is sometimes needed when a type extends or implements a parameterized class or interface. | |
|
The compiler insert bridge methods in subtypes of parameterized
supertypes to ensure that subtyping works as expected.
Example (before type erasure): interface Comparable <A> {In the example, class NumericValue implements interface Comparable<NumericValue> and must therefore override the superinterface's compareTo method. The method takes a NumericValue as an argument. In the process of type erasure, the compiler translates the parameterized Comparable<A> interface to its type erased counterpart Comparable . The type erasure changes the signature of the interface's compareTo method. After type erasure the method takes an Object as an argument. Example (after type erasure): interface Comparable {After this translation, method NumericValue.compareTo(NumericValue) is no longer an implementation of the interface's compareTo method. The type erased Comparable interface requires a compareTo method with argument type Object , not NumericValue . This is a side effect of type erasure: the two methods (in the interface and the implementing class) have identical signatures before type erasure and different signatures after type erasure.
In order to achieve that class
NumericValue
remains a class
that correctly implements the
Comparable
interface, the compiler
adds a bridge method to the class. The bridge method has the same
signature as the interface's method after type erasure, because that's
the method that must be implemented. The bridge method delegates to the
orignal methods in the implementing class.
The existence of the bridge method does not mean that objects of arbitrary types can be passed as arguments to the compareTo method in NumericValue . The bridge method is an implementation detail and the compiler makes sure that it normally cannot be invoked. Example (illegal attempt to invoke bridge method): NumericValue value = new NumericValue((byte)0);The compiler does not invoke the bridge method when an object of a type other than NumericValue is passed to the compareTo method. Instead it rejects the call with an error message, saying that the compareTo method expects a NumericValue as an argument and other types of arguments are not permitted. You can, however, invoke the synthetic bridge message using reflection. But, if you provide an argument of a type other than NumericValue , the method will fail with a ClassCastException thanks of the cast in the implementation of the bridge method. Example (failed attempt to invoke bridge method via reflection): int reflectiveCompareTo(NumericValue value, Object other)The cast to type NumericValue in the bridge method fails with a ClassCastException when an argument of a type other than NumericValue is passed to the bridge method. This was it is guaranteed that a bridge method, even when it is called, will fail for unexpected argument types. |
|
| LINK TO THIS | Technicalities.FAQ102 |
| REFERENCES |
What
is type erasure?
Under which circumstances is a bridge method generated? |
| When a type extends or implements a parameterized class or interface and type erasure changes the signature of any inherited method. | |
|
Bridge methods are necessary when a class implements a
parameterized interface or extends a parameterized superclass and type
ersure changes the argument type of any of the inherited non-static methods.
Below is an example of a class that extends a parameterized superclass. Example (before type erasure): class Superclass <T extends Bound> {Example (after type erasure): class Superclass {Type erasure changes the signature of the superclass's methods. The subclass's methods are no longer overriding versions of the superclass's method after type erasure. In order to make overriding work the compiler adds bridge methods. The compiler must add bridge methods even if the subclass does not override the inherited methods. Example (before type erasure): class Superclass <T extends Bound> {Example (after type erasure): class Superclass {The subclass is derived from a particular instantiation of the superclass and therefore inherits the methods with a particular signature. After type erasure the signature of the superclass's methods are different from the signatures that the subclass is supposed to have inherited. The compiler adds bridge methods, so that the subclass has the expected inherited methods. No bridge method is needed when type erasure does not change the signature of any of the methods of the parameterized supertype. Also, no bridge method is needed if the signatures of methods in the sub- and supertype change in the same way. This can occur when the subtype is generic itself. Example (before type erasure): interface Callable <V> {Example (after type erasure): interface Callable {The return type of the call method changes during type erasure in the interface and the implementing class. After type erasure the two methods have the same signature so that the subclass's method implements the interface's method without a brdige method. However, it does not suffice that the subclass is generic. The key is that the method signatures must not match after type erasure. Otherwise, we again need a bridge method. Example (before type erasure): interface Copyable <V> extends Cloneable {Example (after type erasure): interface Copyable extends Cloneable {The method signatures change to Object copy() in the interface and Triple copy() in the subclass. As a result, the compiler adds a bridge method. |
|
| LINK TO THIS | Technicalities.FAQ103 |
| REFERENCES | What is type erasure? |
| Because the return type of methods of a parameterized type might change as a side effect of type erasure. | |
|
During type erasure the compiler replaces type parameters
by the leftmost bound, or type
Object
if no bound was specified.
This means that methods whose return type is the type parameter would return
a reference that is either the leftmost bound or
Object
, instead
of the more specific type that was specified in the parameterized type
and that the caller expects. A cast is need from the leftmost bound
or
Object
down to the more specific type..
Example (before type erasure): public class Pair<X,Y> {Example (after type erasure): public class Pair {After type erasure the methods getFirst and getSecond of type Pair both have the return type Object . Since the declared static type of the pair in our test case is Pair<String,Long> the caller of getFirst and getSecond expects a String and a Long as the return value. Without a cast this would not work and in order to make it work the compiler adds the necessary casts from Object to String and Long respectively. The inserted casts cannot fail at runtime with a ClassCastException because the compiler already made sure at compile-time that both fields are references to objects of the expected type. The compiler would issue an error method if arguments of types other than String or Long had been passed to the constructor or the set methods. Hence it is guarantees that these casts cannot fail. In general, casts silently added by the compiler are guaranteed not to raise a ClassCastException if the program was compiled without warnings. This is the type-safety guarantee. Implicit casts are inserted when methods are invoked whose return type changed during type erasure. Invocation of methods whose argument type changed during type erasure do not require insertion of any casts. For instance, after type erasure the setFirst and setSecond methods of class Pair take Object arguments. Invoking them with arguments of a more specific type such as String and Long is possible without the need for any casts. |
|
| LINK TO THIS | Technicalities.FAQ104 |
| REFERENCES |
What
is type erasure?
What does type-safety mean? |
| The compiler adds casts as needed. | |
|
In the process of type erasure the compiler replaces type
parameters by their leftmost bound, or type
Object
if no bound
was specified. How does that work if a type parameter has several bounds?
Example (before type erasure): interface Runnable {Example (after type erasure): interface Runnable {The type parameter T is replaced by the bound Callable , which means that both fields are held as references of type Callable . Methods of the leftmost bound (which is Callable in our example) can be called directly. For invocation of methods of the other bounds ( Runnable in our example) the compiler adds a cast to the respective bound type, so that the methods are accessible. The inserted cast cannot fail at runtime with a ClassCastException because the compiler already made sure at compile-time that both fields are references to objects of a type that is within both bounds. In general, casts silently added by the compiler are guaranteed not to raise a ClassCastException if the program was compiled without warnings. This is the type-safety guarantee. |
|
| LINK TO THIS | Technicalities.FAQ105 |
| REFERENCES | What does type-safety mean? |
| A type whose type information is fully available at runtime, that is, a type that does not lose information in the course of type erasure. | |
|
As a side effect of type erasure, some type information
that is present in the source code is no longer available at runtime.
For instance, parameterized types are translated to their corresponding
raw type in a process called
type erasure
and lose the information
regarding their type arguments.
For example, types such as List<String> or Pair<? extends Number, ? extends Number> are available to and used by the compiler in their exact form, including the type argument information. After type erasure, the virtual machine has only the raw types List and Pair available, which means that part of the type information is lost. In contrast, non-parameterized types such as java.util.Date or java.lang.Thread.State are not affected by type erasure. Their type information remains exact, because they do not have type arguments. Among the instantiations of a generic type only the unbounded wildcard instantiations, such as Map<?,?> or Pair<?,?> , are unaffected by type erasure. They do lose their type arguments, but since all type arguments are unbounded wildcards, no information is lost. Types that do NOT lose any information during type erasure are called reifiable types . The term reifiable stems from reification . Reification means that type parameters and type arguments of generic types and methods are available at runtime. Java does not have such a runtime representation for type arguments because of type erasure. Consequently, the reifiable types in Java are only those types for which reification does not make a difference, that is, the types that do not need any runtime representation of type arguments. The following types are reifiable:
|
|
| LINK TO THIS | Technicalities.FAQ106 |
| REFERENCES |
What
is type erasure?
What is reification? What is an unbounded wildcard parameterized type? What is the raw type? Which types can or must not appear as target type in an instanceof expression? Can I create an array whose component type is a concrete parameterized type? Can I create an array whose component type is a wildcard parameterized type? Why is it allowed to create an array whose component type is an unbounded wildcard parameterized type? |
| The type without any type arguments. | |||||||||||
|
The erasure of a parameterized type is the type without
any type arguments (i.e. the raw type). This definition extends to arrays
and nested types.
Examples: The type erasure of a non-parameterized type is the type itself. |
|||||||||||
| LINK TO THIS | Technicalities.FAQ107 | ||||||||||
| REFERENCES | What is the raw type? | ||||||||||
| The type erasure of its leftmost bound, or type Object if no bound was specified. | |||||||||||||||
|
The type erasure of a type parameter is the erasure of
its leftmost bound. The type erasure of an unbounded type parameter is
type
Object
.
Examples:
|
|||||||||||||||
| LINK TO THIS | Technicalities.FAQ108 | ||||||||||||||
| REFERENCES | What is a bounded type parameter? | ||||||||||||||
| A method with the same name and the types of all method parameters replaced by their respective type erasures. | |||||||||
|
The erasure of a method signature is a signature consisting
of the same name and the erasures of all the formal method parameter types.
Examples:
|
|||||||||
| LINK TO THIS | Technicalities.FAQ109 | ||||||||
| REFERENCES |
What
is type erasure?
What is the type erasure of a parameterized type? What is the type erasure of a type parameter? |
||||||||
| There is no perceivable difference. | |||||
|
Some programmers, especially those with a C++ background,
expect that generic code should perform much faster than non-generic code,
because this is one observable benefit of using templates in C++.
Other programmers assume that the synthetic bridge methods and implicit
casts inserted by the compiler in the process of type erasure would degrade
the runtime performance of generic code. Which one is true?
The short answer is: it is likely that one will find neither a substantial
difference in runtime performance nor any consistent trend. However,
this has not yet been verified by any benchmarks I know of. Nevertheless,
let us take a look at the various overlapping effects that might explain
such a statement.
Implicit casts.
The casts added by the compiler are exactly the casts that would appear
in non-generic code. Hence the implicit casts do not add any overhead.
The non-generic code is exactly what the compiler generates in the process
of type erasure, hence there is no difference in performance.
Bridge methods.
The compiler adds bridge methods. These synthetic methods cause an additional
method invocation at runtime, they are represented in the byte code and
increase its volume, and they add to the memory footprint of the program.
It is likely that there is a slight performance penalty for the bridge
method that affects runtime execution and class loading. However,
only new (i.e. 5.0) source code is affected. If we compile legacy
(i.e. 1.4-compatible) source code, there are no additional bridge methods
and the byte code should be identical, more or less, to the way it was
before. Most likely the slight performance penalty is compensated for by
improvements in Hotspot.
Runtime type information.
Static information about type parameters and their bounds is made available
via reflection. This runtime type information adds to the size of
the byte code and the memory footprint, because the information must be
loaded into memory at runtime. Again, this only affects new (i.e.
5.0) source code. On the other hand, there are some enhancements
to reflection that apply even to existing language features, and those
do require slightly larger class files, too. At the same time, the representation
of runtime type information has been improved. For example, there is now
an access bit for "synthetic" rather than a class file attribute, and class
literals now generate only a single instruction. These things often balance
out. For any particular program you might notice a very slight degradation
in startup time due to slightly larger class files, or you might find improved
running time because of shorter code sequences. Yet it is unlikely
that one will find any large or consistent trends.
Compilation time. Compiler performance might decrease because translating generic source code is more work than translating non-generic source code. Just think of all the static type checks the compiler must perform for generic types and methods. On the other hand, the performance of a compiler is often more dominated by its implementation techniques rather than the features of the language being compiled. Again, it is unlikely that one will find any perceivable or measurable trends. |
|||||
| LINK TO THIS | Technicalities.FAQ110 | ||||
| REFERENCES |
How
does the compiler translate Java generics?
What is type erasure? What is a bridge method? Why does the compiler add casts when it translates generics? |
||||
| Use a tool like Retroweaver. | |
| Retroweaver is a Java bytecode weaver that enables you to take advantage of the new 1.5 language features in your source code, while still retaining compability with 1.4 virtual machines. Retroweaver rewrites class files so that they are compatible with older virtual machines. Check out http://sourceforge.net/projects/retroweaver . | |
| LINK TO THIS | Technicalities.FAQ111 |
| REFERENCES |
How
does the compiler translate Java generics?
Retroweaver Download Page |
| Instantiations of generic types have certain super-subtype relationship among each other and have a type relationship to their respective raw type. These type relationships are relevant for method invocation, assignment and casts. | |
|
Relevance of type relationships and type converstion
rules in practice.
The type system of a programming language determines which types are convertible to which other types. These conversion rules have an impact on various areas of a programming language. One area where conversion rules and type relationships play role is casts and instanceof expressions. Other area is assignment compatibility and method invocation, where argument and return value passing relies on convertibility of the involved types. The type conversion rules determine which casts are accepted and which ones are rejected. For example, the types String and Integer have no relationship and for this reason the compiler rejects the attempt of a cast from String to Integer , or vice versa. In contrast, the types Number and Integer have a super-subtype relationship; Integer is a subtype of Number and Number is a supertype of Integer . Thanks to this relationship, the compiler accepts the cast from Number to Integer , or vice versa. The cast from Integer to Number is not even necessary, because the conversion from a subtype to a supertype is considered an implicit type conversion, which need not be expressed explicitly in terms of a cast; this conversion is automatically performed by the compiler whenever necessary. The same rules apply to instanceof expressions. The conversion rules define which types are assignment compatible. Using the examples from above, we see that a String cannot be assigned to an Integer variable, or vice versa, due to the lack of a type relationship. In contrast, an Integer can be assigned to a Number variable, but not vice versa. A side effect of the super-subtype relationship is that we can assign a subtype object to a supertype variable, without an explicit cast anywhere. This is the so-called widening reference conversion ; it is an implicit conversion that the compiler performs automatically whenever it is needed. The converse, namely assignment of a supertype object to a subtype variable, is not permitted. This is because the so-called narrowing reference conversion is not an implicit conversion. It can only be triggered by an explicit cast.
The rules for assignment compatibility also define which objects can
be passed to which method. An argument can be passed to a method
if its type is assignment compatible to the declared type of the method
parameter. For instance, we cannot pass an
Integer
to a
method that asks for
String
, but we can pass an
Integer
to a method that asks for a
Number
. The same rules apply
to the return value of a method.
Super-subtype relationships of parameterized types. In order to understand how objects of parameterized types can be used in assignments, method invocations and casts, we need an understanding of the relationship that parameterized types have among each other and with non-parameterized types. And we need to know the related conversion rules. We already mentioned super-subtype relationships and the related narrowing and widening reference conversions . They exist since Java was invented, that is, among non-generic types. The super-subtype relationship has been extended to include parameterized types. In the Java 5.0 type system super-subtype relationships and the related narrowing/widening reference conversions exist among parameterized types, too. We will explain the details in separate FAQ entries. Here are some initial examples to get a first impression of the impact that type relationships and conversion rules have on method invocation. Consider a method whose declared parameter type is a wildcard parameterized type. A wildcard parameterized type acts as supertype of all members of the type family that the wildcard type denotes. Example (of widening reference conversion from concrete instantiation to wildcard instantiation): void printAll( LinkedList<? extends Number> c) { ... }We can pass a List<Long> as an argument to the printAll method that asks for a LinkedList<? extends Number> . This is permitted thanks to the super-subtype relationship between a wildcard instantiation and a concrete instantiation. LinkedList<Long> is a member of the type family denoted by LinkedList<? extends Number> , and as such a LinkedList<Long> is a subtype of LinkedList<? extends Number> . The compiler automatically performs a widening conversion from subtype to supertype and thus allows that a LinkedList<Long> can be supplied as argument to the printAll method that asks for a LinkedList<? extends Number> . Note that this super-subtype relationship between a wildcard instantiation and a member of the type family that the wildcard denotes is different from inheritance. Inheritance implies a super-subtype relationship as well, but it is a special case of the more general super-subtype relationship that involves wildcard instantiations. We know inheritance relationships from non-generic Java. It is the relationship between a superclass and its derived subclasses, or a super-interface and its sub-interfaces, or the relationship between an interface and its implementing classes. Equivalent inheritance relationships exists among instantiations of different generic types. The prerequisite is that the instantiations must have the same type arguments. Note that this situation differs from the super-subtype relationship mentioned above, where we discussed the relationship between wildcard instantiations and concrete instantiations of the same generic type, whereas we now talk of the relationship between instantiations of different generic types with identical type arguments. Example (of widening reference conversion from one concrete parameterized type to another concrete parameterized type): void printAll( Collection<Long> c) { ... }The raw types Collection and LinkedList have a super-subtype relationship; Collection is a supertype of LinkedList . This super-subtype relationship among the raw types is extended to the parameterized types, provided the type arguments are identical: Collection<Long> is a supertype of LinkedList<Long> , Collection<String> is a supertype of LinkedList<String> , and so on. It is common that programmers believe that the super-subtype relationship among type arguments would extend into the respective parameterized type. This is not true. Concrete instantiations of the same generic type for different type arguments have no type relationship. For instance, Number is a supertype of Integer , but List<Number> is not a supertype of List<Integer> . A type relationship among different instantiations of the same generic type exists only among wildcard instantiations and concrete instantiations, but never among concrete instantiations. Example (of illegal attempt to convert between different concrete instantiations of the same generic type): void printAll( LinkedList<Number> c) { ... }Due to the lack of a type relationship between LinkedList<Number> and LinkedList<Long> the compiler cannot convert the LinkedList<Long> to a LinkedList<Number> and the method call is rejected with an error message. Unchecked conversion of parameterized types. With the advent of parameterized types a novel category of type relationship was added to the Java type system: the relationship between a parameterized type and the corresponding raw type. The conversion from a parameterized type to the corresponding raw type is a widening reference conversion like the conversion from a subtype to the supertype. It is an implicit conversion. An example of such a conversion is the conversion from a parameterized type such as List<String> or List<? extends Number> to the raw type List . The counterpart, namely the conversion from the raw type to an instantiation of the respective generic type, is the so-called unchecked conversion . It is an automatic conversion, too, but the compiler reports an "unchecked conversion" warning. Details are explained in separate FAQ entries. Here are some initial examples to get a first impression of usefulness of unchecked conversions. They are mainly permitted for compatibility between generic and non-generic source code. Below is an example of a method whose declared parameter type is a raw type. The method might be a pre-Java-5.0 method that was defined before generic and parameterized types had been available in Java. For this reason it declares List as the argument type. Now, in Java 5.0, List is a raw type. Example (of a widening reference conversion from parameterized type to raw type): void printAll( List c) { ... }Source code such as the one above is an example of a fairly common situation, where non-generic legacy code meets generic Java 5.0 code. The printAll method is an example of legacy code that was developed before Java 5.0 and uses raw types. If more recently developed parts of the program use instantiations of the generic type List , then we end up passing an instantiation such as List<String> to the printAll method that declared the raw type List as its parameter type. Thanks to the type relationship between the raw type and the parameterized type, the method call is permitted. It involves an automatic widening reference conversion from the parameterized type to the raw type. Below is an example of the conversion in the opposite direction. We consider a method has a declared parameter type that is a parameterized type. We pass a raw type argument to the method and rely on an unchecked conversion to make it work. Example (of an unchecked conversion from raw type to parameterized type): void printAll( List<Long> c) { ... }Like the previous example, this kind of code is common in situation where generic and non-generic code are mixed.
The subsequent FAQ entries discuss details of the various type relationships
and conversions among raw types, concrete parameterized types, bounded
and unbounded wildcard parameterized types.
|
|
| LINK TO THIS | Technicalities.FAQ201 |
| REFERENCES |
What
is the raw type?
What is a wildcard parameterized type? Which super-subtype relationships exist among instantiations of generic types? How does the raw type relate to instantiations of the corresponding generic type? How do instantiations of a generic type relate to instantiations of other generic types that have the same type argument? How do unbounded wildcard instantiations of a generic type relate to other instantiations of the same generic type? How do wildcard instantiations with an upper bound relate to other instantiations of the same generic type? How do wildcard instantiations with a lower bound relate to other instantiations of the same generic type? |
How
does the raw type relate to instantiations of the corresponding generic
type?
| The raw type is the supertype of all instantiations of the corresponding generic type. | |
|
The raw types have the regular supertype-subtype relationship
with other raw types. For illustration we use the collection classes and
interfaces from the JDK (see package
java.util
).
In addition, the raw types are supertypes of all concrete and all wildcard
instantiations of the generic type. For instance, the raw type
Collection
is a supertype of all instantiations of the generic type
Collection
.
With both properties combined a raw type is the supertype of all instantiations of all its generic and non-generic subtypes. For instance, the raw type Collection is a supertype of all instantiations of all generic collection classes.
Regarding conversions, the usual reference widening conversion from subtype to supertype is allowed. That is, every instantiation of a generic type can be converted to the corresponding raw type. The reverse is permitted, too, for reasons of compatibility between generic and non-generic types. It is the so-called unchecked conversion and is accompanied by an "unchecked" warning. In detail, the conversion from a raw type to a concrete or bounded wildcard instantiation of the corresponding generic type leads to a warning. The conversion from the raw type to the unbounded wildcard instantiation is warning-free. |
|
| LINK TO THIS | Technicalities.FAQ202 |
| REFERENCES |
How
do parameterized types fit into the Java type system?
What is the raw type? What is a concrete paramterized type? What is a wildcard parameterized type? What is the unbounded wildcard parameterized type? Which super-subtype relationships exist among instantiations of generic types? |
| An instantiation of a generic type is the supertype of all instantiations of generic subtypes that have the same type argument. | |||
Instantiations of the generic types have supertype-subtype
relationships with concrete instantiations of generic subtypes provided
they all have the exact same type arguments. The example below uses the
JDK collection types (see package
java.util
)
for illustration.
The diagram illustrates the super-subtype relationship among instantiations that have the same type argument. The type argument can be a concrete type, but also a bounded or unbounded wildcard. For instance, Collection<Number> is the supertype of List<Number> and LinkedList<Number> , Collection<? extends Number> is the supertype of List<? extends Number> and LinkedList<? extends Number> , Collection<?> is the supertype of List<?> and LinkedList<?> , and so on. Type relationships to other concrete instantiations do not exist. In particular, the supertype-subtype relationship among the type arguments does not extend to the instantiations. For example, Collection<Number> is NOT a supertype of Collection<Long> .
Regarding conversions, the usual reference widening conversion from subtype to supertype is allowed. The reverse is not permitted. |
|||
| LINK TO THIS | Technicalities.FAQ203 | ||
| REFERENCES |
How
do parameterized types fit into the Java type system?
What is the raw type? What is a concrete parameterized type? What is a wildcard parameterized type? What is the unbounded wildcard parmeterized type? Which super-subtype relationships exist among instantiations of generic types? |
||
| An unbounded wildcard instantiation is the supertype of all instantiations of the generic type. | |
|
The unbounded wildcard instantiation of a generic type
is supertype of all concrete and all wildcard instantiations of the same
generic type.
For instance, the unbounded wildcard instantiation Collection<?> is supertype of all instantiations of the generic type Collection . The example below uses the JDK collection types (see package java.util ) for illustration.
At the same, an unbounded wildcard instantiation is supertype of all unbounded instantiations of any generic subtypes. For instance, the unbounded wildcard instantiation Collection<?> is supertype of List<?> , LinkedList<?> , and so on. Both type relationships combined, an unbounded wildcard instantiation is supertype of all instantiations of the same generic type and of all instantiations of all its generic subtypes.
Regarding conversions, the usual reference widening conversion from subtype to supertype is allowed. The reverse is not permitted. There is one special rules for unbounded wildcard instantiations: the conversion from a raw type to the unbounded wildcard instantiation is not an "unchecked" conversion, that is, the conversion from Collection to Collection<?> does not lead to an "unchecked" warning. This is different for concrete and bounded instantiations, where the conversion from the raw type to the concrete and bounded wildcard instantiation leads to an "unchecked" warning. |
|
| LINK TO THIS | Technicalities.FAQ204 |
| REFERENCES |
How
do parameterized types fit into the Java type system?
Which super-subtype relationships exist among instantiations of parameterized types? What is the raw type? What is a concrete parmeterizd type? What is a wildcard parameterized type? What is the unbounded wildcard parameterized type? What is a wildcard? What is an unbounded wildcard? What is a bounded wildcard? |
| A wildcard instantiation wild an upper bound is supertype of all instantiations of the same generic type where the type argument is a subtype of the upper bound. | |
|
An upper bound wildcard instantiation is supertype of all
concrete instantiations with type arguments that are subtypes of the upper
bound, the upper bound itself being included. For instance,
Collection<?
extends Number>
is supertype of
Collection<Number>
,
Collection<Long>
,
Collection<Short>
,
etc., because
Number
,
Long
, and
Short
are subtypes
(or same type) of
Number
. The underlying idea is: a subtype of
the upper bound (e.g.
Long
) belongs to the family of types that
the wildcard (e.g.
? extends Number
) stands for and in this case
the wildcard instantiation (e.g.
Collection<? extends Number>
)
is a supertype of the concrete instantiation on the subtype of the upper
bound (e.g.
Collection<Long>
).
At the same time, a wildcard instantiation with an upper bound is supertype of all generic subtypes that are instantiated on the same upper bound wildcard. For instance, Collection<? extends Number> is supertype of Set<? extends Number> and ArrayList<? extends Number> .
The upper bound wildcard instantiation is also supertype of other upper bound wildcard instantiation with an upper bound that is a subtype of the own upper bound. For instance, Collection<? extends Number> is supertype of Collection<? extends Long> and Collection<? extend Short> , because Long and Short are subtypes of Number .
Similarly, Collection<? extends Comparable<?>> is supertype of Collection<? extends Number> and Collection<? extends Delayed> , because Number is a subtype of Comparable<Number> , which is a subtype of Comparable<?> , and Delayed (see java.util.concurrent.Delayed ) is a subtype of Comparable<Delayed> , which is a subtype of Comparable<?> . The idea is that if the upper bound of one wildcard is a supertype of the upper bound of another wildcard then the type family with the supertype bound includes the type family with the subtype bound. If one family of types (e.g. ? extends Comparable<?>) includes the other (e.g. ? extends Number> and ? extends Delayed ) then the wildcard instantiation on the larger family (e.g. Collection<? extends Comparable<?>> ) is supertype of the wildcard instantiation of the included family (e.g. Collection<? extends Number> ). All these type relationships combined make a bounded wildcard instantiation supertype of a quite a number of instantiations of the same generic type and subtypes thereof.
Regarding conversions, the usual reference widening conversion from subtype to supertype is allowed. The reverse is not permitted. |
|
| LINK TO THIS | Technicalities.FAQ205 |
| REFERENCES |
How
do parameterized types fit into the Java type system?
Which super-subtype relationships exist among instantiations of parameterized types? What is the raw type? What is a concrete parameterized type? What is a wildcard parameterized type? What is the unbounded wildcard parameterized type? What is a wildcard? What is an unbounded wildcard? What is a bounded wildcard? |
| A wildcard instantiation wild a lower bound is supertype of all instantiations of the generic type where the type argument is a supertype of the lower bound. | |
|
A wildcard instantiation with a lower bound is supertype
of all concrete instantiation with type arguments that are supertypes of
the lower bound, the lower bound itself included. For instance,
Collection<?
super Number>
is supertype of
Collection<Number>
,
Collection<Serializable>
,
and
Collection<Object>
, because
Number
,
Serializable
and
Object
are supertypes (or same type) of
Number
. The
underlying idea is: a supertype of the lower bound (e.g.
Object
)
belongs to the family of types that the wildcard (e.g.
? super Number
)
stands for and in this case the wildcard instantiation (e.g.
Collection<?
super Number>
) is a supertype of the concrete instantiation on the
supertype of the lower bound (e.g.
Collection<Object>
).
At the same time, a wildcard instantiation with a lower bound is supertype of parameterized subtypes that are instantiated on the same lower bound wildcard. For instance, Collection<? super Number> is supertype of Set<? super Number> and ArrayList<? super Number> .
The lower bound wildcard instantiation is also supertype of other lower bound wildcard instantiation with a lower bound bound that is a supertype of the own lower bound. For instance, Collection<? super Number> is supertype of Collection<? super Serializable> , because Serializable is a supertype of Number . The idea is that if the lower bound of one wildcard is a subtype of the lower bound of another wildcard then the type family with the subtype bound includes the type family with the supertype bound. If one family of types (e.g. ? super Number) includes the other (e.g. ? super Serializable ) then the wildcard instantiation on the larger family (e.g. Collection<? super Number> ) is supertype of the wildcard instantiation of the included family (e.g. Collection<? super Serializable> ).
Regarding conversions, the usual reference widening conversion from subtype to supertype is allowed. The reverse is not permitted. |
|
| LINK TO THIS | Technicalities.FAQ206 |
| REFERENCES |
How
do parameterized types fit into the Java type system?
Which super-subtype relationships exist among instantiations of parameterized types? What is the raw type? What is a concrete parameterized type? What is a wildcard parameterized type? What is the unbounded wildcard parameterized type? What is a wildcard? What is an unbounded wildcard? What is a bounded wildcard? |
| This is fairly complicated and the type relationships are best determined setting up tables as explained in this FAQ entry. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Super-subtype relationships among instantiations of generic
types are determined by two orthogonal aspects.
On the one hand, there is the inheritance relationship between a supertype and a subtype. This is the usual notion of inheritance as we know it from non-generic Java. For instance, the interface Collection is a supertype of the interface List . This inheritance relationship is extended in analogy to instantiations of generic types, i.e. to parameterized types. The prerequisite is that the instantiations must have identical type arguments. An example is the supertype Collection<Long> and its subtype List<Long> . The rule is: as long as the type arguments are identical, the inheritance relationship among generic types leads to a super-subtype relationship among corresponding parameterized types. On the other hand, there is a relationship based on the type arguments. The prerequisite is that at least one of the involved type arguments is a wildcard. For example, Collection<? extends Number> is a supertype of Collection<Long> , because the type Long is a member of the type family that the wildcard " ? extends Number " denotes. This kind of type relationship also exists between two wildcard instantiations of the same generic type with different wildcards as type arguments. The prerequisite is that the type family denoted by one wildcard is a superset of the type family denoted by the other wildcard. For example, Collection<?> is a supertype of Collection<? extends Number> , because the family of types denoted by the wildcard " ? " is a superset of the family of types denoted by the wildcard " ? extends Number ". The super-sub set relationship among the type arguments leads to a super-sub type relationship among corresponding instantiations of the same parameterized type. The type relationship mentioned above, between a wildcard instantiation and a concrete instantiation of the same generic type, is a special case of this rule; you just interpret the concrete type argument as a type family with only one member, namely the concrete type itself. Both effects - the super-subtype relationship due to inheritance and the super-subtype relationship due to type arguments - are combined and lead to a two-dimensional super-subtype relationship table. The tables below use examples for illustration.
The vertical axis of the table lists parameterized types according to
their inheritance relationship, starting with the supertype on the top
to a subtype on the bottom. The horizontal axis lists type arguments according
to their super-subset relationship of the type families they denote, starting
with the largest type set on the lefthand side to the smallest type set
on the righthand side.
If you pick a certain entry in the table, say List<? extends Serializable> , then the subtable to the bottom and to the right contains all subtypes of the entry.
Below is another example that involves lower bound wildcards. Again,
the horizontal axis lists type arguments according to their super-subset
relationship of the type families they denote, starting with largest set
of types denoted by the unbounded wildcard "
?
" over type families
of decreasing size to a type set consisting of one concrete type. The difficulty
with lower bound wildcards is that the super-subset relationship of the
type families denoted by lower bound wildcards is slightly counter-intuitive
to determine. Details are discussed in a separate FAQ entry.
If you pick a certain entry in the table, say
List<? super Long>
,
then the subtable to the bottom and to the right contains all subtypes
of the entry and you would find information such as:
ArrayList<?
super Number>
is
a subtype of
List<?
super Long>
.
Generic Types With More Than One Type Parameter We have been setting up tables to determine the super-subtype relationships among different instantiations of different, yet related parameterized types. These tables were two-dimensional because we took into account inheritance on the one hand and various values for a type argument on the other hand. A similar technique can be applied to parameterized types with more than one type parameter.
The example below uses a generic class
Pair
with two type parameters.
The vertical axis lists the first type arguments order by their super-subset
relationship from the largest type set to the smallest type set.
The horizontal axis does the same for the second type argument.
Tables with more than two dimensions can be set up in analogy. The key point is that you list generic types from supertype to subtype and type arguments from superset to subset. For this purpose you need to interpret type arguments as sets of types and determine their super-subtype relationships. Note that the latter can be rather counter-intuitive in case of multi-level wildcards involving lower bounds. Details are discussed in a separate FAQ entry. |
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| LINK TO THIS | Technicalities.FAQ207 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| REFERENCES |
How
do parameterized types fit into the Java type system?
Which super-subset relationships exist among wildcards? What is the raw type? What is a concrete parameterized type? What is a wildcard parameterized type? What is the unbounded wildcard parameterized type? |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| A super-subtype relationship between upper bounds leads to a super-subset relationship between the resulting upper bound wildcards, and vice versa for a lower bounds. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
A wildcard denotes a family (or set)
of types. These type sets can have super-subset relationships, namely when
one type set includes the other type set. The super-subset relationship
among wildcards determines the super-subtype relationships of instantiations
of parameterized types using these wildcards as type arguments. The
super-subtype relationships among instantiations of parameterized types
play an important role in method invocation, assignments, type conversions,
casts and type checks (as was discussed in a previous FAQ entry). For this
reason, we need to know how to determine the super-subset relationships
among the type sets denoted by wildcards.
Here are the rules:
Here are some examples illustrating the rules:
The wildcard " ? " denotes the largest possible type set, namely the set of all types. It is the superset of all type sets. Upper bound wildcards are fairly easy to understand, compared to lower bound wildcards. The wildcard " ? extends Serializable " denotes the family of types that are subtypes of Serializable , the type Serializable itself being included. Naturally, this type family is a subset of the type set denoted by the wildcard " ? ". The wildcard " ? extends Number " denotes the family of types that are subtypes of Number , the type Number itself being included. Since Number is a subtype of Serializable , the type family " ? extends Number " is a subset of the type family " ? extends Serializable ". In other words, the super-sub type relationship between the upper bounds leads to a super-sub set relationship between the resulting type sets. The concrete type Long denotes a single-member type set, which is a subset of the type family " ? extends Number " because Long is a subtype of Number .
Among the lower bound wildcards, the wildcard "
? super Long
"
denotes the family of types that are supertypes of
Long
, the type
Long
itself being included. The wildcard "
? super Number
"
denotes the family of types that are supertypes of
Number
, the
type
Number
itself being included.
Number
is a
supertype of
Long
, and for this reason the type family "
?
super Number
" is smaller than the type family "
? super Long
";
the latter includes type
Long
as member, while the former excludes
it. In other words, the super-sub
type
relationship between
the lower bounds leads to the opposite relationship between the resulting
type sets, namely a sub-super
set
relationship.
Multi-Level Wildcards Matters are more complicated when it comes to multi-level wildcards. In principle, the rules outlined above are extended to multi-level wildcards in analogy. However, the resulting super-subset relationships tend to be everything but intuitive to understand, so that in practice you will probably want to refrain from overly complex multi-level wildcards. Nonetheless, we discuss in the following the rules for super-subset relationships among two-level wildcards. For multi-level wildcards we apply the same rules as before. The only difference is that the bounds are wildcards instead of concrete types. As a result we do not consider type relationships among the bounds, but set relationships.
The rules look fairly complex, but basically it is a recursive process.
For instance: the type set denoted by "
Examples:
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| LINK TO THIS | Technicalities.FAQ208 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| REFERENCES |
How
do parameterized types fit into the Java type system?
Which super-subtype relationships exist among instantiations of parameterized types? What is the raw type? What is a concrete instantiation? What is a wildcard instantiation? What is the unbounded wildcard instantiation? |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| No. | |
extends
is an overloaded keyword in the Java programming
language. It has different meanings, depending on the context in
which it appears. The
extends
keyword can appear in four different
locations:
Definition of classes and interfaces Example ( extends in the definition of a class): public class Quadruple<T> extends Triple<T> {This is an example where extends means inheritance. We define a subclass Quadruple that is derived from a superclass Triple . This kind of inheritance exists between non-generic and generic classes. It leads to a super-subtype relationship between the two types. In case of generic types it leads to a super-subtype relationship between instantiations of the two types that have identical type arguments. For instance, Triple<Long> is a subtype of Triple<Long> , and Quadruple<? extends Number> is a subtype of Triple<? extends Number> . Example ( extends in the definition of an interface): public interface SortedSet<E> extends Set<E> {This is another example where extends means inheritance. This time the inheritance relationship exists between two interfaces instead of two classes. We have the super-subtype relationships as for classes. Definition of type parameter bounds Example ( extends in the definition of type parameter bounds): public class Caller<V, T extends Callable<V>> {In this example extends does not mean inheritance. The extends keyword is here used to define the bounds of a type parameter. A common misunderstanding is that the type argument that later replaces the type parameter in an instantiation must inherit from the bounds. This is often the case, but it is not the only option. In our example, we can supply a sub-interface of Callable<V> as a type argument, that is, an interface that extends the bound. But we can also supply a class type that implements the bound. In other words, extends in conjunction with type parameter bounds does not strictly mean inheritance, but it also includes the implements -relationship that exists between classes and interfaces. In conjunction with type parameter bounds the extends keyword refers to an even broader notion of subtyping. It includes relationships that cannot be expressed in terms of extends or implements as we know them from non-generic Java. Consider the following example. Example ( extends in the definition of a type parameter bound that is a final class): public class SomeClass<T extends String> {In this example the bound is a final class. Final classes cannot be inherited from. The only permitted type argument is type String , which is not a type that inherits from the bound; it is the bound itself. When applied to a bound that is a final class, extends does not mean inheritance; it means identity . Let us consider another example that demonstrates that extends in conjunction with type parameter bounds means more than inheritance. Example ( extends in the definition of a type parameter bound that is a wildcard parameterized type): public class SomeClass<T extends Collection<?>> {In this example the bound is a wildcard instantiation of the Collection interface. Wildcard parameterized types cannot be inherited from. In this case extends does not mean inheritance either. It refers to the super-subtype relationship that exists among wildcard parameterized types and concrete parameterized type. Conceivable type arguments would be concrete parameterized type, such as Collection<String> or List<Long> , but also other wildcard parameterized types, such as Collection<? extends Number> . These type arguments do not inherit from the bound, but they are members of the type family that the bound denotes. When applied to a bound that is a wildcard parameterized type, extends does not mean inheritance; it means member of the type family denoted by the wildcard parameterized type. Definition of wildcard bounds Example ( extends in the definition of a wildcard bound): List<? extends Number> ref = new ArrayList<Number>();The meaning of extends in conjunction with wildcard bounds is similar to the meaning of extends in conjuction with type parameter bounds. It does NOT mean that the unknown type that the wildcard stands for (the so-called wildcard capture ) must inherit from the wildcard bound. It can, but it does not have to. The capture can equally well be the bound itself and extends would mean identity instead of inheritance. If the bound is a wildcard parameterized type, then extends refers to the subtype relationship that exists among wildcard parameterized types and concrete parameterized types. Example ( extends in the definition of a wildcard bound): Triple<? extends Collection<?>> q = new Triple<List<? extends Number>>();In this example the wildcard capture cannot be a type that inherits from the bound, because wildcard parameterized types such as Collection<?> cannot be inherited from. Instead the wildcard capture must be a member of the type family denoted by Collection<?> , such as Collection<String> , List<Number> , List<?> or List<? extends Number> . Again, extends does not mean inheritance; it means member of the type family denoted by the wildcard parameterized type. |
|
| LINK TO THIS | Technicalities.FAQ209 |
| REFERENCES |
How
do parameterized types fit into the Java type system?
Which super-subset relationships exist among wildcards? What is the capture of a wildcard? |
| No. Exception and error types must not be generic. | |
| It is illegal to define generic types that are directly or indirectly derived from class Throwable . Consequently, no instantiations of generic type appear anywhere in exception handling. | |
| LINK TO THIS | Technicalities.FAQ301 |
| REFERENCES | Why are generic exception and error types illegal? |
| Because the virtual machine cannot distinguish between different instantiations of a generic exception type. | |
|
A generic class must not directly or indirectly be derived
from class
Throwable
, which means that generic exception or error
types are not allowed. Imagine if they were allowed ...
Example (of illegal generic exception type): class IllegalArgumentException<T> extends Exception { // illegalWe might then want to catch instantiations of this (illegal) generic exception type. Example (of illegal use of illegal parameterized exception type): void method_1() {Taking into account that generic Java source code is translated to Java byte code by type erasure, it should be clear that the method's catch clauses do not make any sense. Both parameterized exception types have the same runtime type and the mechanism for catching exceptions is a runtime mechanism performed by the virtual machine based on the non-exact runtime types. The JVM has no chance to distinguish between different instantiations of the same generic (exception) type. For this reason, generic exception and error types are pointless in Java and banned from the language. (Note that generic exception and error types are not pointless per se, but in the context of Java generics with type erasure their are nonsensical.) Other problems occur when we define methods that throw instantiations of an (illegal) generic exception type. Example (of illegal use of illegal parameterized exception type): void method_1()Again, after type erasure, both parameterized exception types have the same runtime type and the method's throws clause is nonsensical. The method could at best throw the raw type, which means that it must not create and throw any instantiations of the (illegal) generic type either. Another reason to disallow generic exception and error types. |
|
| LINK TO THIS | Technicalities.FAQ302 |
| REFERENCES | What is type erasure? |
| It depends. | |
| Type parameters can appear in throws clauses, but not in catch clauses. | |
| LINK TO THIS | Technicalities.FAQ303 |
| REFERENCES |
Can
I use a type parameter in a catch clause?
Can I use a type parameter in in a throws clause? Can I throw an object whose type is a type parameter? |
| No. | |
|
Using a type parameter in a
catch
clause is nonsensical
because of the translation by type erasure.
Example (before type erasure): < E extends Exception > void someMethod() {Example (after type erasure): void someMethod() {After type erasure the catch clause would boil down to a catch clause using the type parameter's bound. This is because type parameters do not have a runtime type representation of themselves. Type parameters are replaced by their leftmost bound in the course of translation by type erasure. In our example the catch clause for the unknown exception type E is translated by type erasure to a catch clause for type Exception , which is the bound of the type parameter E . This catch clause for type Exception precedes further catch clauses for other exception types, and renders them pointless. In other words, there never is a catch clause for the particular unknown exception type that the type argument stands for. Instead of catching a particular exception type we end up catching the bound of the unknown exception type, which changes the meaning of the sequence of catch clauses substantially and is almost always undesired. For this reason, the use of type parameters in catch clauses is illegal. |
|
| LINK TO THIS | Technicalities.FAQ304 |
| REFERENCES |
Can
I use a type parameter in in a throws clause?
Can I throw an object whose type is a type parameter? |
| Yes. | |
|
Using a type paramter in a
throws
clause is permitted.
Example (before type erasure): public interface Action<E extends Exception> {In this example we see a generic interface Action whose type parameter E is the exception type that its run method throws. This is perfectly reasonable, because throws clauses are a compile-time feature and the lack of a runtime type representation of the type parameter is not needed. At runtime, a particular exception type will have replaced the type parameter E , so that we would throw and catch an object of a concrete exception type. In our example we instantiate the Action interface using the FileNotFound exception type as a type argument, so that a FileNotFound exception is raised and caught. Even after type erasure the code snippet above still captures the intent. Example (after type erasure): public interface Action { |
|
| LINK TO THIS | Technicalities.FAQ305 |
| REFERENCES |
Can
I use a type parameter in a catch clause?
Can I throw an object whose type is a type parameter? |
| In principle, yes, but in practice, not really. | |
|
We can declare methods that throw an exception (or error)
of unknown type.
Example (of method with type parameter in throws clause): interface Task< E extends Exception > {Can such a method throw an object of the unknown exception (or error) type? The method has in principle 3 ways of raising such an exception (or error):
Example (throwing an object of unknown type): final class CleanUp< E extends Exception , T extends Task<E>> { |
|
| LINK TO THIS | Technicalities.FAQ306 |
| REFERENCES |
Can
I create an object whose type is a type parameter?
Can I use a type parameter in a catch clause? Can I use a type parameter in in a throws clause? |
| Using the raw type as the scope qualifier, instead of the any instantiation of the generic type. | |
|
If you refer to a static member of a generic type, the
static member name must be preceded by the name of the enclosing scope,
such as
EnclosingType.StaticMember
. In case of a generic
enclosing type the question is: which instantiation of the generic type
can or must be used as the scope qualifier?
The rule is that no instantiation can be used. The scope is qualified using the raw type. This is because there is only one instance of a static member per type. Example (of a generic type with static members): public final class Counted<T> {In the example above, the generic class Counted has a static member MAX and there is only one unique Counted.MAX , regardless of the number of objects of type Counted and regardless of the number of instantiations of the generic type Counted that may be used somewhere in the program. Referring to MAX as Counted<String>.MAX , Counted<Long>.MAX , Counted<?>.MAX , etc. would be misleading, because it suggests that there were several manifestations of the MAX member, which is not true. There is only one Counted.MAX , and it must be referred to using the raw type Counted as the scope qualifier. The same is true for other static members such as static methods and static nested types. The example illustrates that we must refer to the static method as Counted.getCount and to the static nested exception class as Counted.BeyondThresholdException . In sum, it is illegal to refer to a static member using an instantiation of the generic enclosing type. This is true for all categories of static members, including static fields, static methods, and static nested types, because each of these members exists only once per type. |
|
| LINK TO THIS | Technicalities.FAQ351 |
| REFERENCES |
Is
there one instances of a static field per instantiation of a generic type?
Why can't I use a type parameter in any static context of the generic class? How do I refer to an interface type nested into a generic type? How do I refer to an enum type nested into a generic type? How do I refer to a (non-static) inner class of a generic type? Can I import a particular instantiation of generic type? |
| Using an instantiation of the enclosing generic type as the scope qualifier, instead of the raw type. | |
|
Static nested types are referred to using the raw form
of the enclosing generic type as the scope qualifier. This is different
for inner classes.
Like a static nested type, an inner class exists only once, in the sense that there is only one .class file that represents the inner class. Different from a static nested type, an inner class depends on the type argument of its outer class type. This is because each object of an inner class type has a hidden reference to an object of the outer class type. The type of this hidden reference is an instantiation of the generic enclosing type. As a result, the inner class type is not independent of the enclosing class's type arguments. Example (of an inner class nested into a generic outer class): class Sequence <E> {In the example above, the inner class Iterator depends on the outer class's type parameter E : the type parameter E is the return type of the iterator's getNext method and the inner class access the outer class's array of elements of type E . For this reason, the iterator type is referred to as Sequence<T>.Iterator (using an instantiation), instead of just Sequence.Iterator (using the raw type). This does not imply, that a scope qualification using the raw type is illegal; it is permitted and it means that the outer object being referred to by the inner object is of the raw type instead of a more specific type. In contrast, the nested exception types are static classes and do not depend on the outer class's type parameter. (Static nested types never depend on their enclosing type's type parameters, because the type parameters must not appear in any static context of a generic class. Details are explained in a separate FAQ entry.) Static nested classes must be referred to using the raw type as the scope qualifier, that is, as Sequence.NoElementsException ; use of an instantiation as the scope, such as Sequence<T>.NoElementsException , is illegal. |
|
| LINK TO THIS | Technicalities.FAQ352 |
| REFERENCES |
How
do I refer to static members of a generic type?
Why can't I use a type parameter in any static context of the generic class? |
| Using the raw type as the scope qualifier, instead of the any instantiation of the generic type. | |
|
Nested interfaces are implicitly static. This is sometimes
confusing because the interface looks like it were a non-static member
of its enclosing class, while in fact it is static. As a static member
of a generic type it must be referred to using the raw form of the enclosing
type as the scope qualifier. Using an instantiation as the scope
qualifier is illegal and rejected by the compiler.
Example (of a nested interface): class Controller <E extends Executor > {The Command interface is nested into the generic Controller class. The compiler does not allow that we refer to the nested interface using any instantiation of the enclosing generic class as the scope qualifier. Instead of saying Controller<ExecutorService>.Command we must say Controller.Command . Below is another example of a nested interface taken from the java.util package. The generic Map interface has a nested Entry interface. Example (of a nested interface taken from package java.util ): public interface Map <K,V> {The source code above is an excerpt from the JDK source code. Note that the nested interface Entry is generic itself and has its own type parameters, which are independent of the outer interface's type parameters. The fact that the type parameters of inner and outer interface have the same names, namely K and V , is perhaps confusing, but perfectly legal. The inner interface's type parameters K and V are visible only inside the inner interface and have nothing to do with the outer interface's type parameters K and V . When the inner interface Entry is used it must be referred to using the raw type Map as the scope qualifier, that is, as Map.Entry<String,Long> for instance. A qualification such as Map<String,Long>.Entry<String,Long> is illegal. |
|
| LINK TO THIS | Technicalities.FAQ353 |
| REFERENCES |
Why
can't I use a type parameter in any static context of the generic class?
How do I refer to static members of a parameterized type? Can I import a particular instantiation of parameterized type? |
| Using the raw type as the scope qualifier, instead of the any instantiation of the generic type. | |
|
Nested enum types are implicitly static. This is sometimes
confusing because the enum type looks like it were a non-static member
of its enclosing class, while in fact it is static. As a static member
of a generic type it must be referred to using the raw form of the enclosing
type as the scope qualifier. Using an instantiation as the scope
qualifier is illegal and rejected by the compiler.
Example (of a nested enum type): class Controller <E extends Executor > {The enum type State is nested into the generic Controller class. The compiler does not allow that we refer to the nested interface using any instantiation of the enclosing generic class as the scope qualifier. Instead of saying Controller<ExecutorService>.State we must say Controller.State . The same applies to the enum constants; they are referred to as Controller.State.VALID and Controller.State.INVALID . |
|
| LINK TO THIS | Technicalities.FAQ354 |
| REFERENCES |
How
do I refer to static members of a paramterized type?
Can I import a particular parameterized type? |
| No. | |
|
In an
import
statement we must not use parameterized
types; only raw types are permitted. This applies to regular and
static
import
statements.
Example (of a generic type with static members): package com.sap.util; |
|
| LINK TO THIS | Technicalities.FAQ355 |
| REFERENCES | How do I refer to static members of a parameterized type? |
| Because they do not make sense in Java. | |
|
An enum type is similar to a class type of which only a
limited number of instances, namely the enum values, exist. The enum values
are static fields of the enum type. The key question is: of which type
would the static enum values be if the enum type were allowed to be parameterized?
Example (of an illegal generic enum type): public enum Tag<T> { // illegal, but assume we could do thisThis enum type would be translated to a class that roughly looks like this: public class Tag<T> extends Enum<Tag<T>> {The static enum values cannot be of type Tag<T> because type parameters such a T must not appear in any static context. Should they be of the raw type Tag then? In this case the private attribute field would be of type Object, the invocation of the setAttribute method would be flagged an "unchecked call" and the getAttribute method would only return Object . The entire parameterization would be pointless then. On the other hand, if we wanted that the type of the enum values is a particular instantiation of the generic enum type, how would we tell the compiler? There is no syntax for specifying the type of an enum value. Also, when we refer to the enum values we must qualify their name by the name of their defining class, that is, Tag.good and Tag.bad . Although Tag is a parameterized type, we cannot say Tag<String>.good or Tag<Long>.bad . This is because static members of a generic type must be referred to via the raw type name. In other words, we would not even be capable of expressing that we intend to refer to an enum value belonging to a particular instantiation of the generic enum type. No matter how we put it: generic enum types do not make sense. |
|
| LINK TO THIS | Technicalities.FAQ356 |
| REFERENCES |
Why
can't I use a type parameter in any static context of the generic class?
How do I refer to static members of a parameterized type? |
| The automatic deduction of the type arguments at compile time. | |
|
Type inference happens when the compiler can deduce the
type arguments of a generic type or method from context information.
In this case the type arguments need not be explicitly specified.
There are two situations in which type inference is attempted:
class ArrayList<E> {Class ArrayList<E> is a generic class. Usually you must specify the type parameter whenever you use the generic type (unless you want to use the raw type). In an instance creation expression you can omit the type parameter and replace it by empty angle brackets. When the compiler sees the empty angle brackets in the new -expression it takes a look at the lefthand side of the assignment in which the new -expression appears. From the static type of the lefthand side variable the compiler infers the type parameter of the generic type of the newly created object. In the example above the compiler concludes that the type variable E must be replaced by the type String . If you neither specify the type parameter no use the empty angle brackets you refer to the raw type. [Note: Type inference for new -expressions was introduced in Java 7 and did not exist in Java 5 and 6.] Example (of automatic type inference on method invocation): class Collections {The copy() method is a generic method. When the generic method is invoked without explicit specification of the type argument then the compiler takes a look at the arguments that are provided for the method call. From their static types the compiler infers the type parameter of the generic copy() method. In the example above the compiler concludes that the type variable T must be replaced by the type String . Different from type inference in conjunction with new-expressions empty angle bracket are not permitted; the brackets must be omitted entirely. |
|
| LINK TO THIS | Technicalities.FAQ400 |
| REFERENCES |
What
is type argument inference for generic methods?
What is type argument inference for instance creation expressions? What is the diamond operator? Is there a correspondence between type inference for method invocation and type inference for instance creation? |
| Yes, the instance creation expression for a generic type is treated like invocation of a generic creator method. | |
The rules for type inference
for method
invocation and type inference for instance creation
are the same.
Consider a generic class with a constructor:
class SomeClass<T> {The creation of an object of this type can involve type inference, e.g. in this example: SomeClass<Long> ref = new SomeClass<>(0L);The type inference for the instance creation is performed in the same way as for a static creator method. If the class had the following creator method: class SomeClass<T> {then the type inference for the invocation of the creator method would yield the same result as type inference for the instance creation. That is, the following leads to equivalent type inference: SomeClass<Long> ref = new SomeClass<>(0L); |
|
| LINK TO THIS | Technicalities.FAQ400A |
| REFERENCES |
What
is type argument inference for instance creation expressions?
What is type argument inference for generic methods? |
| It denotes the empty angle brackets that are used for type inference in new -expressions. | |
|
Since Java 7 the compiler can infer the type parameters
of a parameterized type in a
new
-expression. In order to
trigger the type inference the so-called
diamond operator
is used.
Below is an example.
Example (of diamond operator): List<String> list1 = new ArrayList <String> ();The empty angle brackets <> are called the diamond operator . The diamond operator is not really an operator in the sense of the syntax specification of the Java programming language. Rather it is just an empty type parameter specification. The empty brackets are needed in order to distinguish between the raw type ArrayList and the incomplete type ArrayList<> . Until Java 9 the diamond operator was not permitted in anonymous inner class definitions. Here is an example: Callable<Long> task = new Callable <> () { // error in Java 7/8; fine since Java 9 |
|
| LINK TO THIS | Technicalities.FAQ400B |
| REFERENCES |
What
is type argument inference for instance creation expressions?
Why does the type inference for an instance creation expression fail? |
| The automatic deduction of the type arguments in a new -expression. | |
|
When an object of a parameterized type is created using
a
new
-expression (also called an
instance creation expression
)
then the compiler can infer part of the type information of the object
to be created. More specifically, the compiler can deduce the type parameters
of the parameterized type if it is incomplete. In order to trigger
the automatic type inference the so-called
diamond operator
is used
(see
Technicalities.FAQ400A
).
[Note: Type inference for new-expressions is available since Java 7.]
Type inference takes into account the context in which the new -expression appears and what the new -expression looks like. The type inference process works in two separate steps:
Since Java 8, an additional inference context is permitted, namely the method invocation context . If the new -expression is the argument to a method invocation, then the compiler deduces the missing type parameter information from the method's declared argument type, if possible. Examples (of type inference for an instance creation expression without constructor arguments): List<String> list1 = new ArrayList <String> ();In the examples above no constructor arguments are specified. As a result, the compiler cannot deduce any type information from the constructor arguments. Instead it takes a look at the static type of the expression on the lefthand side of the assignment and infers the missing type parameters from there. Examples (of improved type inference in Java 8): List<String> list1 = new ArrayList<> (); // fine since Java 7In both examples type inference is needed, because no constructor arguments are specified. The first new -expression appears in an assignment context and the compiler infers the missing type parameter String from the left-hand side of the assignment. The second new -expression appears in a method invocation context. Before Java 8, this yields a compile-time error. Since Java 8, the compiler infers the missing type parameter String from the declared type of the argument of the synchronizedList method. (Actually, it is a little more complicated. The synchronizedList method is a generic method and the compiler must first infer the generic method's type parameter before it knows the method's declared argument type. The generic synchronizedList method appears in an assignement context, from which the compiler deduces that the type parameter for synchronizedList must be String . Its declared argument type then is List<String> and from this information the compiler infers that the new ArrayList must be an ArrayList<String> .) The inference is different if constructor arguments are provided. In such a context the compiler takes a look at the static types of the constructor arguments and ignores the lefthand side of the assignment. Examples (of type inference for an instance creation expression with constructor arguments): Set<Long> s1 = new HashSet<>();- The first new -expression does not have constructor arguments; the missing type parameter is inferred from the lefthand side of the assignment. The lefthand side is of type Set<Long> and compiler concludes that the missing type parameter must be Long . - The second new -expression has constructor arguments; the missing type parameter is inferred from the constructor argument. The constructor argument is of type List<Long> and the compiler again concludes that the missing type parameter must be Long . Note, the lefthand side of the assignment is ignored because the constructor argument provides enough information for the type inference to complete successfully. - The third new -expression demonstrates that the lefthand side of the assignment is indeed ignored (in Java 7). The compiler again infers from the constructors argument, i.e., the result of the asList method, that the missing type parameter for the new HashSet must be Long . This leads to a type mismatch and an according error message. The compiler does not conclude that the missing type parameter should be Number because it ignores the lefthand side of the assignment. In Java 8, the type inference was modified and improved. Since then, compiler infers Number as the type parameter form the new HashSet on the right-hand side of the compiler and from that deduces Number as the type parameter for the asList method. In Java 8, this compiles just fine.
- The fourth
new
-expression does not rely on type inference
and simply specifies the intended type parameter explicitly.
There is yet a different result of type inference if the context of the new -expression has neither constructor arguments nor a lefthand side of an assignment. Here is an example. Examples (of type inference for an instance creation expression in a method invocation context): void method(Set<Long> arg) { ... }The new -expressions appears as the argument of a method invocation. In the first invocation there is neither a constructor argument nor a lefthand side of an assignment. For lack of more specific information the compiler in Java 7 concludes that the missing type parameter must be Object . This leads to a type mismatch and an according error message. This changed with Java 8. The method invocation context is now a permitted type inference context. The compiler concludes from the declared argument type of method that the new HashSet must be a HashSet<Long> . The second invocation is fine because the compiler can infer the missing type parameter from the constructor argument. |
|
| LINK TO THIS | Technicalities.FAQ400C |
| REFERENCES |
What
is the diamond operator?
Why does the type inference for an instance creation expression fail? |
| Usually because there is not enough context information. | |
|
Occasionally, the type inference for instance creation
expressions yields results that are surprising at first sight and might
lead to unexpected compile-time errors. Here is an example.
The result of type inference can be surprising for a new -expression that appears in a context other than an assignment. Example (of surprising type inference): String s = new ArrayList<>().iterator().next(); // errorIn the example above an error message is issued because the new -expression new ArrayList <> () does not have constructor arguments and it neither appears on the right-hand side of an assignment nor as the argument of a method invocation. Instead, it appears in a chain of method calls. Such a chain is not a valid type inference context. Hence the compiler has no information for type inference and concludes that the missing type parameter of the ArrayList is type Object . The iterator's next method then returns an Object instead of the expected String and the compiler accordingly reports a type mismatch. |
|
| LINK TO THIS | Technicalities.FAQ400D |
| REFERENCES |
What
is type argument inference for generic methods?
What is type argument inference for instance creation expressions? Is there a correspondence between type inference for method invocation and type inference for instance creation? Why do temporary variables matter in case of invocation of generic methods? |
| The automatic deduction of the type arguments of a generic method at compile time. | |
A generic method can be invoked in two ways:
class Collections {In this example, the max method is invoked like a regular method and the compiler automatically infers the type argument from the type of the method argument. In our example the compiler finds that the formal method parameter is Collection<A> and that the actual method argument is of type LinkedList<Long> . From this information the compiler concludes that A must be replaced by Long , which yields an applicable max method with the signature Long max(Collection<Long>) . |
|
| LINK TO THIS | Technicalities.FAQ401 |
| REFERENCES |
What
is a parameterized or generic method?
What is explicit type argument specification? What happens if a type parameter does not appear in the method parameter list? Why doesn't type argument inference fail when I provide inconsistent method arguments? |
What
is explicit type argument specification?
| Providing a type argument list when the method is invoked. | |
|
A generic method can be invoked with or without an explicit
type argument specification. If you do not want to rely on the compiler's
automatic type argument inference process you can specify the type arguments
explicitly.
Example (of a generic method and its invocation): public class Utilities {The max method can be invoked as Utilities.<String>max("abc","xyz") , where the type argument is explicitly provided, or as Utilities.max("abc","xyz") , in which case the compiler automatically infers the type argument. The syntax for explicit type argument specification requires that the type argument list is precedes the method name, like in Utilities.<String>max . Note the difference to parameterized types, where the type argument list follows the type name, like in List<String> . There is a little syntax quirk in the explicit type argument specification: it is required that the type argument(s) is preceded by either a type (for a static method like the one in the example), or the object on which the method is invoked (for a non-static method), or super (in case that a superclass's method is invoked). This is even required when methods are invoked in the scope of the same class. In the scope of the class we usually omit the type or object on which the method is invoked; we simply say method() rather than this.method() . This shorthand is not permitted when a type argument list precedes the method name. We must not say <String>method() , instead we must say this.<String>method() . Example: class SomeClass { |
|
| LINK TO THIS | Technicalities.FAQ402 |
| REFERENCES |
What
is a parameterized or generic method?
How do I invoke a generic method? What is type argument inference? Can I use a wildcard as the explicit type argument of a generic method? |
Can
I use a wildcard as the explicit type argument of a generic method?
| No, wildcards are not permitted as explicit type arguments of a generic method. | |
|
When a generic method is invoked,
then we usually rely on the compiler to infer the type argument of the
generic method. In rare cases, we specify the type argument explicitly.
Here is an example where explicit type argument specification would make
sense.
Example (of generic methods): class Factory { Example (of using the factory methods): public static void main(String[] args) {The factory is configured (using the setComponentType() method) to create lists that are backed by an array of strings. When we invoke a factory method then the compiler performs type inference and tries to figure out what the best type argument for invocation of the generic make() method would be. Since the type parameter does not appear in the method's arguments list the compiler infers the type argument from the context in which the return value is used. As long as we assign the result of invoking the make() method to a reference of type List<String> , all is fine. The compiler infers T:=String when the make() method is invoked. We can place strings into the list, but no other type of object, because the reference of type List<String> will not permit it. If we try assigning the result of invoking the make() method to a reference of type List<Date> , it will compile as well. The compiler will again infer the type argument from the usage of the return value; this time it infers T:=Date . However, when we attempt placing a date into the list, which is backed by a string array internally, an ArrayStoreException will be raised. Basically, what we created is a list that will always raise ArrayStoreException s. This undesired situation is a side effect of the "unchecked cast" warning in the implementation of the make() method. In order to prevent the undesired situation, the make() method is best invoked with an explicitly specified type argument. Example (of explicit type argument specification): public static void main(String[] args) {Basically, the example shows a situation in which explicit type argument specification serves a purpose and is helpful. In the example we have been using a concrete type when we specified the type argument explicitly, which raises the question: can we also use wildcards as explicit type arguments? The answer is: No, wildcards are not permitted as explicit type arguments of parameterized method. Example (of a wildcard as explicitly specified type arguments of generic methods): List<? super String> list1 = Factory.make(10); // fine |
|
| LINK TO THIS | Technicalities.FAQ402A |
| REFERENCES |
What
is type argument inference?
What is explicit type argument specification? What happens if a type parameter does not appear in the method parameter list? |
What
happens if a type parameter does not appear in the method parameter list?
| The compiler tries to infer the type argument from the calling context. | |
|
If the type parameter does not appear in the types of the
method arguments, then the compiler cannot infer the type arguments by
examining the types of the actual method arguments. If the type parameter
appears in the method's return type, then the compiler takes a look at
the context in which the return value is used. If the method call
appears as the righthand side operand of an assignment, then the compiler
tries to infer the method's type arguments from the static type of the
lefthand side operand of the assignment.
Example (for inference from assignment context): public final class Utilities {The create method is generic and the type parameter T does not appear in the method parameter list; it appears in the method's return type HashSet<T> though. The result of the method is assigned to a variable of type HashSet<Integer> so that the compiler infers that the type argument of the create method must be T:=Integer . The compiler is even smart enough to infer the type argument of the create method if the method result is assigned to a variable of a supertype of HashSet<Integer> , such as Collection<Integer> . The invocation of a generic method might appear as the argument of another method invocation. Such an invocation context was not considered for type inference before Java 8. This changed with the improved type inference in Java 8. In Java 5, 6, and 7, the compiler does not try to perform any special type inference when the invocation of a generic method appears in a method invocation context. Instead the compiler handles the method call as though it would appear in no context. No context means that the compiler performs the type inference algorithm as though the method result was assigned to a variable of type Object . Example (for inference from a method invocation context): public final class Utilities {In Java 5, 6, and 7, the compiler treats the call Utilities.print(Utilities.create(10)) like it were an assignment Object o = Utilities.create(10)) . A lefthand side of type Object does not provide any particular type information so that the compiler cannot really infer anything. If no specific type can be inferred then the compiler chooses Object as the type argument. With type Object as the type argument the create method returns a HashSet<Object> , which is incompatible to a HashSet<String> and leads to the error message displayed above. In Java 8, the compiler considers that the print method needs an argument of type HashSet<String> and figures out that the type parameter for the create method must be String then. The key difference is that previously the method invocation context was treated like no context for type inference and in Java 8 it a valid type inference context, from which the compiler retrieves information for the type deduction process. If the type argument inference does not lead to the desired result or if we want to disable the automatic inference, we can explicitly specify the type arguments. Example (for explicit type argument specification): public final class Utilities { |
|
| LINK TO THIS | Technicalities.FAQ403 |
| REFERENCES |
What
is a parameterized or generic method?
What is type argument inference? What is explicit type argument specification? Why doesn't type argument inference fail when I provide inconsistent method arguments? Why do temporary variables matter in case of invocation of generic methods? |
| Because the "inconsistent" arguments might make sense to the compiler. | |
|
Occasionally the compiler infers a type where we might
expect that no type can be inferred.
Example (of surprising type argument inference): This is the example of a method whose type argument appears in several method arguments. Quite obviously the intent is that the component type of the array should match the type of the second argument. For this reason we might expect that a method invocation such as Utilities.fill (new String[5], new Integer(1 00)) would fail, because the argument types String[] and Integer are inconsistent. However, the compiler does not reject this method call. Instead it performs type inference and infers the common supertypes of String and Integer as type argument. To be precise the compiler infers which is a synthetic type construct used internally by the compiler. It denotes the set of supertypes of String and Integer . Whether the result of this successful type inference is desired or not depends on the circumstances. In this example the source code compiles, but the method invocation in question will fail at runtime with an ArrayStoreException , because the method would try to store integers in an array of strings. In Java 5, 6, and 7 it was possible to prevent the perhaps undesired type argument inference by a minor modification of the fill method. Example (modified, in Java 5, 6, and 7): In Java 5, 6, and 7 the compiler inferred both type arguments separately as String and Since Java 8 the compiler does no longer issue an error message and you will observe the same behavior as in the initial example without the additional type variable " S extends T ". The fact that the type inference process in Java 5, 6, and 7 could not find a common super type and issued an error message was just an accident. The glitch was fixed in Java 8 and nowadays the compiler exploits the covariance of arrays in this example, too. Example (same as above, but in Java 8):
|
|
| LINK TO THIS | Technicalities.FAQ404 |
| REFERENCES |
What
is a parameterized or generic method?
What is type argument inference? What is explicit type argument specification? What happens if a type parameter does not appear in the method parameter list? Why do temporary variables matter in case of invocation of generic methods? |
| Because of the automatic type argument inference. | |
Usually, it does not make a difference whether
we use the result of a method call for invocation of the next method, like
in
f(x).g();or whether we store the result first in a temporary variable and then pass the temporary variable to the next method, like in tmp=f(x);The effect is usually the same. However, when the methods are generic methods, it may well make a difference. Example: Let us consider a chained method call where the result of the create method is used for calling the iterator method. We can perform the chained method call in two steps using a temporary variable: Or we can perform it one step: If the methods were non-generic methods the result of both invocation techniques would be the same. Not so in our example, where the first method is generic and the compiler must infer the type parameter from the context. Obviously, the context is different in a chained call compared to the use of temporaries. In the two-step invocation the result of the create method appears in an assignment, and assignment is a context that the compiler considers for type inference. In the one-step invocation the result of the create method is used to invoke another method, and method chains are not considered for type inference. Here is what the compiler infers for the two-step call: For inference of the type argument of the create method the compiler takes a look at the type of the left-hand side of the assignement, namely the type of the temporary variable. It is of type HashSet<Integer> and the compiler infers T:=Integer . The one-step call, in contrast, does not compile because the type argument inference works differently in this case: For inference of the type argument of the create method the compiler does not consider any context information, because it neither appears in an assignment nor a method invocation context, but in a method chain instead. It treats the invocation of create as though the result of create were assigned to a variable of type Object , which does not provide any type information to deduce anything from. For this reason the compiler infers T:=Object , which yields an instantiation of the create method with the signature HashSet<Object> create() . Equipped with this information the compiler finds that the iterator method return an Iterator<Object> , which leads to the error message.
The example demonstrates, that in rare cases there can
be a difference between a one-step nested method call such as
f(x).g();
and a two-step call using a temporary variable such as
tmp=f(x); tmp.g();
.
The difference stems from the fact that an assignment context is considered
for the type argument inference, while other situations are not.
|
|
| LINK TO THIS | Technicalities.FAQ405 |
| REFERENCES |
What
is a parameterized or generic)method?
What is type argument inference? What is explicit type argument specification? What happens if a type parameter does not appear in the method parameter list? Why doesn't type argument inference fail when I provide inconsistent method arguments? |
| An anonymous type variable that represents the particular unknown type that the wildcard stands for. The compiler uses the capture internally for evaluation of expressions and the term "capture of ?" occasionally shows up in error message. | |
|
A wildcard is compatible with all types from a set of types.
For instance, all instantiations of the generic type
List
, such
as
List<String>
,
List<Number>
,
List<Long>
,
etc. can be assigned to a reference variable of type
List<?>
.
Wildcards are typically used as argument or return types in method signatures.
The goal and effect is that the method accepts arguments of a larger set
of types, namely all types that belong to the type family that the wildcard
denotes.
Example (of a wildcard in a method signature): public static void reverse(List<?> list) {The reverse method accepts arguments that are of a type that is an instantiation of the generic type List . In order to use the type argument list of type List<?> the compiler converts the wildcard instantiation to the so-called capture of the wildcard. The capture represents the particular albeit unknown type of the argument that is actually passed the method. This particular unknown type is, of course, a member of the type family that the wildcard denotes. The wildcard capture can be imagined as an anonymous type variable that the compiler generates internally and uses as the static type of the method parameter. A type variable is like a type parameter of a generic type or method; it stands for a particular unknown type. Changing the method parameter's type from the instantiation using a wildcard to an instantiation using the capture is known as capture conversion . The translation of our method can be imagined as though the compiler had re-implemented the method to use a synthetic generic method that has the wildcard capture as it type parameter. Example (pseudo code showing the wildcard capture): private static <T_?001> void reverse_?001(List<T_?001> list) {For the analysis and translation of the implementation of the reverse method the compiler will use List<T_?001> as the type of the method parameter. The synthetic type variable T_?001 is used for all purposes where the method parameter's static type information is needed, like for instance, in type checks and for type inference. For illustration, let us consider a conceivable implementation of the reverse method using a generic helper method rev for which the compiler must infer the type arguments from the wildcard. Example (implementation of the reverse method): private static <T> void rev(List<T> list) {The compiler applies capture conversion and generates an anonymous type variable. In principle, the compiler translates the reverse method to something like this. Example (pseudo code showing the wildcard capture): private static <T> void rev(List<T> list) {Among other things, the compiler uses the wildcard capture T_?001 for inference of the type argument T of the generic helper method rev . It infers T:=T_?001 and therefore invokes the instantiation <T_?001>rev of method rev . |
|
| LINK TO THIS | Technicalities.FAQ501 |
| REFERENCES | What is a wildcard capture assignment-compatible to? |
| Nothing, except the unbounded wildcard itself. | |
|
The capture of a wildcard is compatible to a corresponding
wildcard, never to a concrete type.
Example (implementation of assignment of wildcard instantiation): private static void method( List<?> list) {The method takes an argument of type List<?> , which the compiler translates to type List<capture of ?> . The wildcard capture denotes a particular unknown type. From the static type information "capture of ?" the compiler cannot tell, whether capture stands for String ; the capture could stand for any type. On this ground the compiler rejects the assignment of the method result of type List<capture of ?> to the variable of type List<String> in the example. |
|
| LINK TO THIS | Technicalities.FAQ502 |
| REFERENCES |
What
is the capture of a wildcard?
What does type-safety mean? |
| No, not even if the bound is a final class. | |
|
The capture of a wildcard is compatible to a corresponding
wildcard, never to a concrete type. Correspondingly, the capture of a bounded
wildcard is compatible solely to other wildcards, but never to the bound.
For illustration we use the getClass method, which is defined in class Object (see java.lang.Object.getClass ). The result of the getClass method is of type Class<? extends X> , where X is the erasure of the static type of the expression on which getClass is called. Example (with bounded wildcard): Number n = new Integer(5);In our example the static type of the expression on which getClass is called is Number . Hence the return value is of type Class<capture of ? extends Number> . A variable of type Class<capture of ? extends Number> can refer to a Class<Number> , a Class<Long> , a Class<Integer> , etc. There's no guarantee that is actually refers to a Class<Number> , and indeed, in our example it refers to a Class<Integer> . The compiler rightly complains. How do we fix it? Example (corrected): Number n = new Integer(5);Since getClass returns a wildcard instantiation we must assign the result to a wildcard instantiation. Class<?> would be correct, but also the more specific type Class<? extends Number> would work. Interestingly, the capture of a bounded wildcard, whose upper bound is a final class, is still incompatible to the bounds type, although the set of types that the bounded wildcard denotes contains only one type, namely the bounds type itself. Example (using final class as bound): String s = new String("abc"); |
|
| LINK TO THIS | Technicalities.FAQ503 |
| REFERENCES |
What
is the capture of a wildcard?
What is a wildcard capture assignment-compatible to? |
| It depends on the kind of wildcard. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Using an object through a reference variable of a wildcard
parameterized type is restricted. Consider the following class:
Example ( of a parameterized class): class Box<T> {In a wildcard parameterized type such as Box<?> the type of the field and the argument and the return types of the methods would be unknown. It is like the field t would be of type " ? " and the put method would take an argument of type " ? " and so on. In this situation the compiler does not let us assign anything to the field or pass anything to the put method. The reason is that the compiler cannot make sure that the object that we are trying to assign to the field or pass as an argument to a method is of the expected type, since the expected type is unknown. Similar effects can be observed for methods such as like equalTo and clone , which have a parameterized argument or return type and the type parameter T appears as type argument of the parameterized argument or return type. Below is a table that lists which uses of fields and methods are legal or illegal. It assumes a class like this: class X< T > {Examples and further explanations can be found in subsequent FAQ entries.
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| LINK TO THIS | Technicalities.FAQ601 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| REFERENCES |
What
is a wildcard instantiation?
What is a wildcard? What is an unbounded wildcard? What is a bounded wildcard? Which methods that use the type parameter in the argument or return type are accessible in an unbounded wildcard parameterized type? Which methods that use the type parameter in the argument or return type are accessible in an upper bound wildcard parameterized type? Which methods that use the type parameter in the argument or return type are accessible in a lower bound wildcard parameterized type? Which methods that use the type parameter as type argument of a parameterized argument or return type are accessible in a wildcard parameterized type? Which methods that use the type parameter as upper wildcard bound in a parameterized argument or return type are accessible in a wildcard parameterized type? Which methods that use the type parameter as lower wildcard bound in a parameterized argument or return type are accessible in a wildcard parameterized type? In a wildcard parameterized type, can I read and write fields whose type is the type parameter? |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| We cannot call methods through an unbounded wildcard parameterized type that take arguments of the "unknown" type. But we can call methods that return objects of the "unknown" type. | |
Example:
class Box< T > {We cannot call the put method of the Box type through a reference variable of type Box<?> , because the method takes an argument of the unknown type that the wildcard stands for. From the type information Box<?> the compiler does not know whether the object we are passing to the method is compatible with the actual object contained in the box. If the Box<?> would be refering to a Box<Long> , then it would clearly violate the type guarantees if we could put a string into the box that is supposed to contain a long value. The only argument that is accepted it the null reference, because it has no type. The same reasoning applies to all methods that take an argument of the "unknown" type, even if the method does not even modify the box, like the contains method. It just takes an object of the "unknown" type and compares it. If a string is passed to the contains method of a box that contains a long value, it simply returns false . No harm is done. Yet the invocation is illegal if performed through a reference variable of type Box<?> . [Defining the contains method as taking an argument of type Object , instead of T , would avoid this effect. In this case the contains method would not take an object of "unknown", but an object of "any" type, and it would be permitted to invoke it through a reference variable of type Box<?> .] We can freely invoke any methods that neither take nor return objects of the "unknown" type. The example demonstrates that methods returning an object of the unknown type can be called and return an object of unknown type, which can be assigned to a reference variable of type Object , but to not to a reference variable of a more specific type. |
|
| LINK TO THIS | Technicalities.FAQ602 |
| REFERENCES |
Which
methods and fields are accessible/inaccessible through a reference variable
of a wildcard type?
Which methods that use the type parameter in the argument or return type are accessible in an unbounded wildcard parameterized type? Which methods that use the type parameter in the argument or return type are accessible in an upper bound wildcard parameterized type? Which methods that use the type parameter in the argument or return type are accessible in a lower bound wildcard parameterized type? Which methods that use the type parameter as type argument of a parameterized argument or return type are accessible in a wildcard parameterized type? Which methods that use the type parameter as upper wildcard bound in a parameterized argument or return type are accessible in a wildcard parameterized type? Which methods that use the type parameter as lower wildcard bound in a parameterized argument or return type are accessible in a wildcard parameterized type? In a wildcard instantiation, can I read and write fields whose type is the type parameter? What is a wildcard instantiation? What is a wildcard? What is an unbounded wildcard? What is a bounded wildcard? |
| We can call methods through an unbounded wildcard parameterized type that take arguments of the "unknown" type. But we cannot call methods that return objects of the "unknown" type. | |
|
Compared to the rules for upper bound wildcard parameterized
types the rules for wildcard parameterized types with a lower bound wildcard
are the other way round.
We can call methods through an unbounded wildcard parameterized type that take arguments of the "unknown" type. But we cannot call methods that return objects of the "unknown" type. Example: class Box<T> {Methods that take an argument of the "unknown" type can be invoked with either null or an argument whose type is the lower bound or a subtype thereof. That is, we can pass a Long to method put through the reference of type Box<? super Long> . But we cannot pass a Number as an argument, because the compiler does not know whether the Box<? super Long> refers to a Box<Number> or perhaps to a Box<Comparable<Long>> , in which case a Number were inacceptable, because it is not comparable. Methods that return a value of the "unknown" type can be invoked, but only if no assumptions are made regarding the type of the returned object and it is treated like an Object . |
|
| LINK TO THIS | Technicalities.FAQ604 |
| REFERENCES |
Which
methods and fields are accessible/inaccessible through a reference variable
of a wildcard type?
Which methods that use the type parameter in the argument or return type are accessible in an unbounded wildcard parameterized type? Which methods that use the type parameter in the argument or return type are accessible in an upper bound wildcard parameterized type? Which methods that use the type parameter in the argument or return type are accessible in a lower bound wildcard parameterized type? Which methods that use the type parameter as type argument of a parameterized argument or return type are accessible in a wildcard parameterized type? Which methods that use the type parameter as upper wildcard bound in a parameterized argument or return type are accessible in a wildcard parameterized type? Which methods that use the type parameter as lower wildcard bound in a parameterized argument or return type are accessible in a wildcard parameterized type? In a wildcard instantiation, can I read and write fields whose type is the type parameter? What is a wildcard instantiation? What is a wildcard? What is an unbounded wildcard? What is a bounded wildcard? |
| We cannot call methods that use the type parameter as type argument in an argument type. We can call methods that use the type parameter as type argument in the return type; the returned value is accessible through a wildcard instantiation of the return type. | |
|
In a wildcard parameterized type, we cannot call methods
that use the type parameter as type argument in an argument type
in an argument type. But, we can call methods that use the type parameter
as type argument in the return type; the returned value is accessible through
a wildcard instantiation of the return type. The wildcard instantiation
of the return type corresponds to the wildcards instantiation that was
used for the method invocation, e.g. a method of
Box<? extends Number>
would return a
ReturnType<? extends Number>
and a a method
of
Box<? super Number>
would return a
ReturnType<? super
Number>
.
Unbounded wildcard parameterized type. Example (access through unbounded wildcard): class Box< T > {We cannot call the equalTo method of the Box type through a reference variable of type Box<?> , because the compiler does not know which type of object is expected as an argument of the equalTo method. In our example the reference variable unknownBox refers to a Box<String> and hence only a Box<String> would be acceptable as an argument of the equalTo method. But from the type information Box<?> the compiler does not know which type of box would be acceptable as an argument. For this reason the compiler rejects all attempts to invoke the equalTo method. Invocation of the copy method is permitted. It returns an object of an unknown instantiation of the Box type to which we can refer through a variable of type Box<?> . More specific information about the instantiation of the Box type is not available and for this reason the assigment to a reference variable of an instantiation different from Box<?> fails.
In the example, the parameterized argument and return type happens to
be the enclosing type. This is just by chance. The rules explained
above apply in the same way to unrelated parameterized argument and return
types. For instance, if the
Box
class had methods taking
or returning a
Comparable<T>
the same rules would apply: methods
that take arguments of type
Comparable<T>
cannot be called
and methods that return a
Comparable<T>
can be called and the
result can be assigned to a
Comparable<?>
.
A note on the error messages and the term "capture of":
Bounded wildcard parameterized types. The example above used a reference variable of the unbounded wildcard type Box<?> . If we use a bounded wildcard type such as Box<? extends Number> or Box<? super Number> the same rules apply. Example (access through bounded wilcdard): class Box< T > {The equalTo method cannot be called through a reference variable of type Box<? extends Number> because the argument type is still an unknown type. Thanks to the upper bound the compiler knows that the expected argument type must be an instantiation of Box for a type argument that is a subtype of Number , but the compiler still does not know which instantiation exactly. Hence, the compiler cannot make sure that the right type of argument is provided for the method invocation and rejects the method invocation. The effect is exactly the same as for Box<?> , and likewise for Box<? super Number> . The copy method can be called and the in this case the result can be assigned to a reference variable of the more specific type Box<? extends Number> , instead of just Box<?> . When invoked on a Box<? super Number> the result would be assignable to a Box<? super Number> . |
|
| LINK TO THIS | Technicalities.FAQ605 |
| REFERENCES |
Which
methods and fields are accessible/inaccessible through a reference variable
of a wildcard type?
Which methods that use the type parameter in the argument or return type are accessible in an unbounded wildcard parameterized type? Which methods that use the type parameter in the argument or return type are accessible in an upper bound wildcard parameterized type? Which methods that use the type parameter in the argument or return type are accessible in a lower bound wildcard parameterized type? Which methods that use the type parameter as type argument of a parameterized argument or return type are accessible in a wildcard parameterized type? Which methods that use the type parameter as upper wildcard bound in a parameterized argument or return type are accessible in a wildcard parameterized type? Which methods that use the type parameter as lower wildcard bound in a parameterized argument or return type are accessible in a wildcard parameterized type? In a wildcard instantiation, can I read and write fields whose type is the type parameter? What is a wildcard instantiation? What is a wildcard? What is the capture of a wildcard? |
| We cannot call methods that use the type parameter as an upper wildcard bound in an argument type, with an exception for access through a lower bound wildcard parameterized type. We can call methods that use the type parameter as the upper wildcard bound in the return type; the returned value is accessible through the unbounded wildcard instantiation of the return type. | |
|
In an upper bound wildcard parameterized type, we cannot
call methods that use the type parameter as an upper wildcard bound
in an argument type. This holds for access through unbounded and upper
bound wildcard parameterized types. Access through a lower bound
wildcard parameterized type is possible for certain argument types.
We can call methods that use the type parameter as the upper wildcard bound in the return type; the returned value is accessible through the unbounded wildcard instantiation of the return type. This holds for access through unbounded and lower bound wildcard parameterized types. Access through an upper bound wildcard parameterized type yields a more specific return type, namely a return type that corresponds to the upper bound wildcard instantiation that was used for the method invocation, e.g. a method of Box<? extends Number> would return a ReturnType<? extends Number> . Unbounded wildcard parameterized type. Example (access through unbounded wildcard): class Box< T > {We cannot call the takeContentFrom through a reference variable of type Box<?> , because the compiler does not know which type of object is expected as an argument of the takeContentFrom method. From the error message you can tell what the compiler find, namely a method with the signature takeContentFrom(Box<? extends capture of ?>) . The term Box<? extends capture of ?> stands for an instantiation of Box with a type argument of an unknown type that is a subtype of another unknown type. In essence, the argument type is unknown and the compiler has no chance to perform any type checks to make sure the correct type of argument is passed to the method call. And hence the invocation is illegal. Invocation of the getContentType method is permitted. The return value is of a type that is an unknown instantiation of the Class type to which we can refer through a variable of type Class<?> . More specific information about the instantiation of the Class type is not available and for this reason the assigment to instantiations such as Class<? extends Number> as Class<Number> fails. Upper bound wildcard parameterized type. Let us see what the situation is like when we use a bounded wildcard parameterized type instead of the unbounded one. Example (access through upper bound wildcard): class Box< T > {In an upper bound wildcard parameterized type such as Box<? extends Number> the behavior is the same as in an unbounded wildcard parameterized type. The invocation of takeFromContent is rejected because the argument type in unknown. The argument type is Box<? extends capture of ? extends Number> which is an instantiation of Box for an unknown subtype of an unknown subtype of Number . Invocation of the getContentType method is permitted. The return type is more specific than for the unbounded wildcard case; we can refer to the result through a variable of type Class<? extends Number> . This is because the return type is Class<capture of ? extends capture of ? extends Number> , which is an unknown subtype of an unknown subtype of Number , that is, a subtype of Number . Lower bound wildcard parameterized type. Last but no least, the invocation through a lower bound wildcard. Example (access through lower bound wildcard): class Box< T > {The key difference for lower bound wildcards is that the takeContentFrom method can be called for certain argument types, namely for those types that are members of the type family denoted by Box<? extends Number> in our example. It is basically as though the takeContentFrom method in Box<? super Number> had the signature takeContentFrom(Box<? extends Number>) . Why is this? The compiler determines the signature of the takeContentFrom method in Box<? super Number> as takeContentFrom(Box<? extends capture of ? super java.lang.Number>) . Now, what does " ? extends capture of ? super java.lang.Number " mean? It is an unknown subtype of an unknown supertype of Number . No matter what this unknown supertype of Number may be, the subtypes of Number would conform to this description. Invocation of the getContentType method is permitted, as expected. Perhaps surprising is that fact that the return type is not Class<? super Number> , but only Class<?> . This is because the return type is Class<capture of ? extends capture of ? super Number> , which is an unknown subtype of an unknown supertype of Number . Just imagine the unknown supertype of Number were Object , then it could by any type. Hence we know nothing about the return type except that it is an instantiation of class Class and Class<?> correctly describes it. |
|
| LINK TO THIS | Technicalities.FAQ606 |
| REFERENCES |
Which
methods and fields are accessible/inaccessible through a reference variable
of a wildcard type?
Which methods that use the type parameter in the argument or return type are accessible in an unbounded wildcard parameterized type? Which methods that use the type parameter in the argument or return type are accessible in an upper bound wildcard parameterized type? Which methods that use the type parameter in the argument or return type are accessible in a lower bound wildcard parameterized type? Which methods that use the type parameter as type argument of a parameterized argument or return type are accessible in a wildcard parameterized typ? Which methods that use the type parameter as upper wildcard bound in a parameterized argument or return type are accessible in a wildcard parameterized type? Which methods that use the type parameter as lower wildcard bound in a parameterized argument or return type are accessible in a wildcard parameterized type? In a wildcard instantiation, can I read and write fields whose type is the type parameter? What is a wildcard instantiation? What is a wildcard? What is the capture of a wildcard? |
| We can call methods that use the type parameter as a lower wildcard bound in the return type; the returned value is accessible through a wildcard instantiation of the return type (which one depends on the wildcard parameterized type being used). We can call methods that use the type parameter as a lower wildcard bound in an argument type; the argument type is restricted depending on the wildcard being use. | |
|
In a lower bound wildcard parameterized type, we can call
methods that use the type parameter as a lower wildcard bound in the return
type. The returned value is accessible through a wildcard instantiation
of the return type. In an unbounded wildcard parameterized type and
an upper bound wildcard parameterized type the return type is the unbounded
wildcard instantiation of the return type. In a lower bound wildcard parameterized
type the return type is more specific, namely the lower bound wildcard
instantiation of the return type. That is, a method in class
Box
that return
Comparable<? super T>
would return
Comparable<?>
in
Box<?>
and
Box<? extends Number>
and would return
Comparable<?
super Number>
in
Box<? super Number>
.
This matches the return types of methods with an upper bound wildcard;
a method in class
Box
that return
Comparable<? extends
T>
would return
Comparable<?>
in
Box<?>
and
Box<?
super Number>
and would return
Comparable<? extends Number>
in
Box<? extends Number>
. The reasoning for lower bound
wildcard parameterized types is the exactly the same as for upper bound
wildcard parameterized types. The more specific return type in a
lower bound wildcard parameterized type stems from the fact that the return
type of a method that returns
Comparable<? super T>
in
the instantiation
Box<? super Number>
would be
Comparable<capture
of ? super capture of ? super Number>
, which boils down to
Comparable<?
super Number>
.
More interesting is the invocation of methods that take arguments with lower wildcard bounds. We can call methods that use the type parameter as a lower wildcard bound in an argument type. In an unbounded wildcard parameterized type and a lower bound wildcard parameterized type the only permitted argument type is the argument type instantiated for type Object . That is, a method in class Box that takes an argument of type Comparable<? super Number> would in the parameterized types Box<?> and Box<? super Number> accept arguments of Comparable<Object> . Note, this is different from methods that use the type parameter as a upper wildcard bound in an argument type; they cannot be invokeds at all. The reason for this permitted argument type is that such a method would have the signature method(Comparable<? super capture of ?>) in an unbounded parameterized type such as Box<?> , and "? super capture of ?" denotes an unknown supertype of an unknown type. The ultimate supertype of all types is Object , hence Comparable<Object> is permitted as an argument type. And likewise in Box<? super Number> , where the signature would involve "? super capture of ? super Number", and again Object is the only type that would fit. In an upper bound wildcard parameterized type with upper bound Bound the permitted arguments types are the types that belong to the family denoted by ArgumentType<? super Bound> . That is, a method in class Box that takes an argument of type Comparable<? super Number> would in the instantiation Box<? extends Number> accept arguments from the type family Comparable<? super Number> . Note, this is similar to a method with an upper bound argument type in an lower bound parameterized type; e.g. a method in class Box that takes an argument of type Comparable<? extends Number> would in the parameterized type Box<? super Number> accept arguments from the type family Comparable<? extends Number> . The reason for the permitted argument types is that the compiler determines the signature of such a method in Box<? extends Number> as method (Box<? super capture of ? extends Number>) . " super capture of ? extends Number " means unknown supertype of an unknown subtype of Number , in other words all supertypes of Number . Unbounded wildcard parameterized type. Example (access through unbounded wildcard): class Box< T > {The example shows that method compareTo can only be invoked for arguments of type Comparable<Object> and that the return type of method copy is Comparable<?> . Upper bound wildcard parameterized type. Example (access through upper bound wildcard): class Box< T > {The example shows that method compareTo can only be invoked for arguments from the type family denoted by Comparable<? super Number> and that the return type of method copy is Comparable<?> . Lower bound wildcard parameterized type. Example (access through lower bound wildcard): class Box< T > {The example shows that method compareTo can only be invoked for arguments of type Comparable<Object> and that the return type of method copy is Comparable<? super Number> . |
|
| LINK TO THIS | Technicalities.FAQ607 |
| REFERENCES |
Which
methods and fields are accessible/inaccessible through a reference variable
of a wildcard type?
Which methods that use the type parameter in the argument or return type are accessible in an unbounded wildcard parameterized type? Which methods that use the type parameter in the argument or return type are accessible in an upper bound wildcard parameterized type? Which methods that use the type parameter in the argument or return type are accessible in a lower bound wildcard parameterized type? Which methods that use the type parameter as type argument of a parameterized argument or return type are accessible in a wildcard parameterized type? Which methods that use the type parameter as upper wildcard bound in a parameterized argument or return type are accessible in a wildcard parameterized type? Which methods that use the type parameter as lower wildcard bound in a parameterized argument or return type are accessible in a wildcard parameterized type? In a wildcard parameterized type, can I read and write fields whose type is the type parameter? What is a wildcard instantiation? What is a wildcard? What is the capture of a wildcard? |
| It depends on the kind of wildcard. | |
|
In a wildcard parameterized type a field whose type is
the type parameter of the enclosing generic class is of unknown type. Depending
on the wildcard (whether it is unbounded or has an upper or lower bound)
different kinds of access are permitted or disallowed.
Unbounded wildcards. Assignment to a field of the "unknown" type is rejected because the compiler cannot judge whether the object to be assigned is an acceptable one. Read access to a field of the "unknown" type is permitted, but the field has no specific type and must be refered to through a reference of type Object . Example: class Box<T> { Wildcards with an upper bound. The same rules apply to wildcard parameterized type with an upper bound wildcard. Example: class Box<T> {The only difference that a field of "unknown" type is known to be compatible to the upper bound. Hence we can assign the field to a reference variable of type Number in our example. Wildcards with a lower bound. The rules for wildcard parameterized types with a lower bound wildcard are the other way round. We can assign null or a value whose type is the lower bound or a subtype thereof, but we cannot assign a value that is of a supertype of the lower bound. And we must not make any assumptions regarding the type of the field; it must be treated like an Object . Example: class Box<T> { |
|
| LINK TO THIS | Technicalities.FAQ608 |
| REFERENCES |
Which
methods and fields are accessible/inaccessible through a reference variable
of a wildcard type?
Which methods that use the type parameter in the argument or return type are accessible in an unbounded wildcard parameterized type? Which methods that use the type parameter in the argument or return type are accessible in an upper bound wildcard parameterized type? Which methods that use the type parameter in the argument or return type are accessible in a lower bound wildcard parameterized type? Which methods that use the type parameter as type argument of a parameterized argument or return type are accessible in a wildcard parameterized type? Which methods that use the type parameter as upper wildcard bound in a parameterized argument or return type are accessible in a wildcard parameterized type? Which methods that use the type parameter as lower wildcard bound in a parameterized argument or return type are accessible in a wildcard parameterized type? In a wildcard parameterized type, can I read and write fields whose type is the type parameter? What is a wildcard instantiation? What is a wildcard? What is an unbounded wildcard? What is a bounded wildcard? |
Is
it really impossible to create an object whose type is a wildcard parameterized
type?
| It is actually illegal and the compiler tries to prevent it, but there's a workaround. | |
|
The creation of objects of a wildcard
parameterized type is discouraged: it is illegal that a wildcard parameterized
type appears in a
new
expression.
Example (of illegal creation of objects of a wildcard parameterized type ): class Sequence<E> {The compiler rejects all attempts to create an object of wildcard types such as Sequence <?> or Sequence <? super String> . The compiler's effort to prevent the creation of objects of a wildcard parameterized type can easily be circumvented. It is unlikely that you will ever want to create an object of a wildcard parameterized type, but should you ever need one, here's the workaround. Instead of directly creating the object of the wildcard parameterized type via a new expression you define a generic factory method that creates the object. Example (of creation of objects of a wildcard parameterized type; workaround ): class Factory {The trick is defining a generic factory method that takes a dummy argument for each constructor that you had otherwise used. This dummy argument must be a of a generic type and is needed so that the compiler can infer the type argument when the factory method is invoked. When the method is invoked with a dummy argument that is of a wildcard type, then the compiler infers that the factory method's type argument is that particular wildcard and consequently the factory method creates an object of the corresponding wildcard parameterized type. In the example the make method is first invoked twice with a dummy argument of the unbounded wildcard type Dummy<?> . The type argument T is infered as T:=? (or more precisely T:=capture of ? ) and hence the make method creates an object of type Sequence <?> . The subsequent three invocations use a dummy argument of the unbounded wildcard type Dummy<? super String> . Consequently, the compiler infers T as T:=? super String (or more precisely T:=capture of ? super String ) . In addition, the compiler infers S as S:=String and checks that S is a subtype of T . As mentioned above, it is unlikely you will ever need this workaround. |
|
| LINK TO THIS | Technicalities.FAQ609 |
| REFERENCES |
What
is a wildcard parameterized type?
Can I create an object whose type is a wildcard parameterized type? What is type argument inference? |
| Only reifiable types are permitted. | |
|
It is a compile-time error if the reference type mentioned
after the
instanceof
operator does not denote a reifiable type.
In other words, concrete and bounded wildcard parameterized types are NOT
permitted in an
instanceof
expression.
Examples: Object o = new LinkedList<Long>();
System.out.println (o
instanceof List);
The reason for disallowing non-reifiable types (i.e., instantiations of a generic type with at least one type arguments that is a concrete type or a bounded wildcard) in instanceof expression is that these parameterized types do not have an exact runtime type representation. Their dynamic type after type erasure is just the raw type. The evaluation of an instanceof expression could at best check whether the object in question is an instance of the raw type. In the example, the expression (o instanceof List<Long>) would check whether o is an instance of type List , which is a check different from what the source code suggests. In order to avoid confusion, the non-reifiable types are prohibited in instanceof expression. Only the reifiable types (i.e., the raw type and the unbounded wildcard parameterized type) are permitted in an instanceof expression. The reifiable types do not lose any type information during translation by type erasure. For this reason, the instanceof check makes sense and is allowed. |
|
| LINK TO THIS | Technicalities.FAQ701 |
| REFERENCES |
What
is a reifiable type?
What is an unbounded wildcard instantiation? |
| When a subtype redefines a method that was inherited from a supertype. | |
|
Overriding is what we do when we derive subtypes from supertypes
and specialize the subtype's behaviour by means of implementing in the
subtype a specialized version of a method inherited from the supertype.
Overriding is one of the key concepts of object-oriented programming.
Example (of method overriding): class Super { Example (of polymorphic method dispatch): Super ref1 = new Super(); Example (of an overriding method that implements a supertype's method): interface Callable<V> { |
|
| LINK TO THIS | Technicalities.FAQ801 |
| REFERENCES |
What
is method overriding?
What is method overloading? What is the @Override annotation? What is a method signature? What is a subsignature? What are override-equivalent signatures? When does a method override its supertype's method? Can a method of a non-generic subtype override a method of a generic supertype? Can a method of a generic subtype override a method of a generic supertype? Can a generic method override a generic one? Can a non-generic method override a generic one? Can a generic method override a non-generic one? Why doesn't method overriding work as I expect it? |
| When a class has two methods with the same name, but signatures that are not override-equivalent. | |
|
Method overloading happens among methods defined in the
same type, when the methods have the same name and differ in the number
or type of arguments.
Example (of method overloading): class Container { Example (of method overloading): class Super { Example (of overload resolution): Sub ref = new Sub(); Example (of method overloading): class Container { Generic methods can be overloaded as well. Example (of overloaded generic methods): class SomeClass { Method overloading is sometimes confused with method overriding . Overriding happens among a method in a subtype and a method with the same name inherited from a supertype, if the signatures of those methods are override-equivalent . This means that methods in the same class never override each other; they can only overload each other. But methods in a subtype can either override or overload a supertype's method with the same name. It is sometimes difficult to tell the difference between overloading and overriding. The rule is: the subtype method overloads the supertype method if it is not override-equivalent. Override-equivalence is explained in more detail in FAQ entry FAQ812 . Example (of method overloading vs. overriding): class Super { The rules for overriding are more complex than sketeched out above; they are discussed in detail in the subsequent FAQ entries. In particular, overriding does not only require override-equivalent signatures, but additionally a compatible return type and a compatible throws clause. No such rules apply to overloading. Example (of overloaded methods with different return types and throws clauses): class SomeClass { Example (of conflicting erasures): class SomeClass { |
|
| LINK TO THIS | Technicalities.FAQ802 |
| REFERENCES |
What
is overload resolution?
Why doesn't method overloading work as I expect it? |
| An annotation that you can attach to a method so that the compiler issues an error if the annotated method does not override any supertype method. | |
|
Overriding and overloading are often confused. If
you are in doubt, you can annotate a subtype's method with the standard
annotation
java.lang.@Override
.
The compiler then checks whether the annotated method overrides a supertype
method. If not, it reports an error.
Example (of using the @Override annotation): class Super { |
|
| LINK TO THIS | Technicalities.FAQ803 |
| REFERENCES |
What
is method overriding?
What is method overloading? |
| An identification of a method that consist of the method's name and its arguments types. | |||||||||||||||||||||||||||||||||||
|
A method
signature
consists of the method's name
and its arguments types. This is different from a method
descriptor
,
which is the signature plus the return type. In case of generic methods,
the type parameters are part of the signature. Method signatures
play a role in overloading and overriding.
Examples:
The terms $Bound 1 &...&Bound n denote the type variable's bounds. For the purpose of a signature, the order of the bounds is irrelevant, that is, " A extends I&J " is equivalent to " B extends J&I ". Just imagine the compiler would order the bounds alphabetically. |
|||||||||||||||||||||||||||||||||||
| LINK TO THIS | Technicalities.FAQ810 | ||||||||||||||||||||||||||||||||||
| REFERENCES |
What
is method overriding?
What is a subsignature? What are override-equivalent signatures? When does a method override its supertype's method? |
||||||||||||||||||||||||||||||||||
| A method signature that is identical to another method signature, or identical to the erasure of another method signature. | |
|
Subsignatures are relevant in conjunction with overriding.
A prerequisite for overriding is that the subtype method's signature is
a subsignature of the supertype method's signature.
A signature is a subsignature of another signature if:
class Super {
Note, that in the case of the
setNames
method an unchecked
warning is issued. The reasons for this warning are discussed in FAQ entry
FAQ812
.
The unchecked warning is issued whenever an argument type of the superclass
method is a parameterized type and the corresponding argument type in the
subclass method is a raw type, unless the parameterized type is the unbounded
wildcard instantiation of the generic type.
|
|
| LINK TO THIS | Technicalities.FAQ811 |
| REFERENCES |
What
is method overriding?
What is a method signature? What are override-equivalent signatures? When does a method override its supertype's method? |
| Two method signatures where one signature is a subsignature of the other. | |
|
Override-equivalent signatures are relevant in conjunction
with overriding and overloading. We talk of
override-equivalent
signatures when a subtype method's signature is a subsignature of a supertype
method's signature.
The term subsignature was explained in FAQ entry FAQ811 : A signature is a subsignature of another signature if the signature is identical to the other signature or identical to the erasure of the other signature. Override-equivalence is a prerequisite of method overriding.
class Super { In constrast, the two versions of the setName method taking a String argument in the super- and subclass override . This is because they have identical and therefore override-equivalent signatures. In the example above, the erasure of the methods is irrelevant, because none of the methods has a generic argument type. Let us consider an examples that involves generic types. Example (overloading and overriding): class Super { In constrast, the two versions of the printCollection method taking a Collection<?> argument in the superclass and a Collection argument in the subclass override although the methods do not have identical signatures. But the signatures are almost identical: the subclass signature is the erasure of the superclass signature. Note, that the converse is not permitted. If the superclass method had a signature that is identical to the erasure of a subclass method, the compiler would issue an error message. This is neither overloading nor overriding, but just a name clash. Example (neither overloading nor overriding): class Super {The notion of override-equivalent signatures is slightly more complex among generic methods. See FAQ820 and subsequent entries for further details and examples. |
|
| LINK TO THIS | Technicalities.FAQ812 |
| REFERENCES |
What
is method overriding?
What is a method signature? What is a subsignature? When does a method override its supertype's method? Can a method of a non-generic subtype override a method of a generic supertype? Can a method of a generic subtype override a method of a generic supertype? Can a generic method override a generic one? Can a non-generic method override a generic one? Can a generic method override a non-generic one? |
| When the subtype's method has a signature that is a subsignature of the supertype's method and the two methods have compatible return types and throws clauses. | ||||
A subtype's method overrides a supertype's method if the
subtype's method has a signature that is:
First an example where the subtype's method has the same signature as the supertype's method. Example (of overriding methods with identical signatures): class Super { Second, an example where the subtype's method has a signature whose erasure is identical to the signature of the supertype's method. Example (of overriding methods with generics involved): class Super { This kind of overriding is permitted in order to allow that legacy supertypes can be re-engineered and generified without affecting any existing subtypes of the legacy supertype. Imagine the supertype had originally not used any generics. In that original situation, the signatures of supertype and subtype method had been identical. After generification of the supertype the signatures are different. Without the rule that the subtype method can be the erasure of the supertype method all subtypes would also need to be re-engineered and generified. Fortunately, the additional rule renders this re-engineering effort unnecessary and the generification of the supertype does not affect overriding methods in subtypes.
There are additional rules for overriding. When a subtype's method overrides a supertype's method, then:
Let us first consider some examples of substitutable and incompatible return types before we look into conflicting throws clauses. Example (incompatible return types): class Super {
Example (substitutable return types): class Super { Note, that in addition to the regular super-/subtype relationship, an instantiation of a generic type is also considered a subtype of the corresponding raw type. The getLinks method in the code snippet above illustrates this kind of substitutable return types: the supertype method returns the raw type Map and the subtype method returns the instantiation Map<String,File> . The converse is also permitted, namely a supertype version of a method that returns an instantiation and a subtype version that return the corresponding raw type. But this kind of overriding is not type-safe unless the supertype method's return type is an unbounded wildcard instantiation, as illustrated in the code sample below. Example (substitutable return types): class Super { warning: getLinks() in Sub overrides getLinks() in Super;
The supertype and the overriding subtype method must not have conflicting throws clauses. Example (conflicting throws clauses): class Super { Example (compatible throws clauses): class Super { |
||||
| LINK TO THIS | Technicalities.FAQ813 | |||
| REFERENCES |
What
is method overriding?
What is a method signature? What is a subsignature? What are override-equivalent signatures? What are covariant-return types? What are substitutable return types? Can a method of a non-generic subtype override a method of a generic supertype? Can a method of a generic subtype override a method of a generic supertype? Can a generic method override a generic one? Can a non-generic method override a generic one? Can a generic method override a non-generic one? Why doesn't method overriding work as I expect it? |
|||
| Return types of overriding methods where the supertype method's return type is a supertype of the subtype method's return type. | |
|
When a method defined in a subtype overrides a method defined
in a supertype then there are certain rules for the return types of those
methods. Either the return types are identical, or the subtype method's
return type is a raw type that is identical to the supertype method's return
type. Java allows one exception from this rule: the subtype
method's return type is allowed to be a subtype of the supertype method's
return type, or the subtype method's return type is a raw type that is
identical to a subtype of the supertype method's return type.
Example (substitutable return types): class Super { |
|
| LINK TO THIS | Technicalities.FAQ814 |
| REFERENCES |
What
is method overriding?
What is method overloading? What is the @Override annotation? What is a method signature? What is a subsignature? What are override-equivalent signatures? When does a method override its supertype's method? What are substitutable return types? |
| The return type of an overriding subclass method must be substitutable for the superclass method's return type. | |
|
Substitutable return types are required when a method overrides
another method. In that situation the subtype method's return type
must be substitutable for the supertype method's return type.
A subtype method's return type R Sub is considere substitutable for a supertype method's return type R Super if:
Let's take a look at a couple of examples. In the following code snippet the overriding method has the same return type as its superclass method. Example (plain substitutable return types): class Super { Example (covariant substitutable return types): class Super { The converse is permitted, too, but is generally not type-safe. The safe situation is when the subtype's method returns a raw type and the supertype's method returns the wildcard instantiation of the corresponding generic type or of a generic supertype. Example (raw substitutable return types): class Super { When the supertype method's return type is an instantiation different from a wildcard instantation, then the substitution by the raw type in the subclass method is not type-safe and the compiler issues a warning. Example (raw substitutable return types): class Super { |
|
| LINK TO THIS | Technicalities.FAQ815 |
| REFERENCES |
What
is method overriding?
What is method overloading? What is the @Override annotation? What is a method signature? What is a subsignature? What are override-equivalent signatures? When does a method override its supertype's method? What are covariant-return types? Can a method of a non-generic subtype override a method of a generic supertype? Can a method of a generic subtype override a method of a generic supertype? Can a generic method override a generic one? Can a non-generic method override a generic one? Can a generic method override a non-generic one? What is overload resolution? How does overload resolution work when generic methods are involved? Why doesn't method overloading work as I expect it? Why doesn't method overriding work as I expect it? |
| Yes. | |
|
This FAQ entry discusses whether and when methods of a
regular,
non-generic
subtype override methods inherited from a
generic
supertype. The question comes up because overriding requires that
the method signatures are override-equivalent. If the supertype is
generic then its method signatures might involve a type parameter.
At the same time, the subtype is non-generic and its method signatures
will
not
involve type parameters. Hence the signatures are
different, which raises the question whether overriding is possible when
the supertype is generic and the subtype is not.
The answer is: Yes, it is possible. A non-generic type can extend or implement a concrete instantiation of a generic supertype and then its methods can override methods from the generic supertype. Here is an example: Example (of a non-generic subtype with overriding methods): class Super <T> {The subtype methods override the corresponding supertype methods. The subtype derives from a certain instantiation of the supertype, namely Super<Number> in the example. For this reason, all signatures in the subtype, where T is replaced by Number , are override-equivalent signatures. Let us consider some deviations from this straigth-forward case. What if we defined the following methods in the subclass? Example (of a non-generic subtype with redefined supertype methods): class Super <T> {The first set method in the subclass is rejected because it has the same erasure as its superclass's version of the set method, namely void set(Object) . The second set method in the subclass is not override-equivalent, because its signature is truly different from the supertype method's signature; they have a different argument type. For this reason it overloads the set method, instead of overriding it. Let us consider the get methods. What if we defined the following methods in the subclass? Example (of a non-generic subtype with redefined supertype methods): class Super <T> { The Sub.get() method that returns Long has an override-equivalent signature, because its return type is a subtype of the supertype method's return type (covariant return type). Naturally, the two get methods cannot coexist in the same class anyway, because their signatures are the same and they only differ in the return type. |
|
| LINK TO THIS | Technicalities.FAQ820 |
| REFERENCES |
What
is method overriding?
What is method overloading? What is the @Override annotation? What is a method signature? What is a subsignature? What are override-equivalent signatures? When does a method override its supertype's method? What are covariant-return types? What are substitutable return types? Can a method of a generic subtype override a method of a generic supertype? Why doesn't method overriding work as I expect it? |
| Yes, but make sure you do not inadvertently overload instead of override. | |
|
This FAQ entry discusses whether and when methods of a
generic
subtype can override methods of a
generic
supertype. Method
signatures in the sub- and the supertype may involve type parameters of
the respective enclosing class. What is the required relationship
between type variables in sub- and supertype methods in order to allow
for method overriding?
The answer is: Yes, methods of a generic subtype can override methods of a generic supertype, but it is not always trivial to get it right and it is common that mistakes are made and that overriding is confused with overloading. Let us start with a simple example: Example (of a generic subtype with override-equivalent methods): class Super <T> { The remainder of this FAQ entry discusses slightly more complex overriding situation for further illustration of the principle.
Here is another example of successful overriding. Example (of a generic subtype with override-equivalent methods): class Super <T> { However, if we slightly change the subtype, our attempt to override the inherited methods goes wrong. We declare the subtype methods using the type parameter B although we derive from the supertype instantiation on type parameter A . Example (of a generic subtype without override-equivalent methods): class Super <T> {
The
get
methods have identical signatures, but incompatible
return types. Again, because the compiler distinguishes between different
type parameters. For this reason the subtpye method
get
is
rejected with an error message.
Let us modify the subtype a second type and see what happens now. The modification is that the second type parameter is bounded by the first type parameter. Example (of a generic subtype without override-equivalent methods): class Super <T> {
Let us consider a situation where the subtype's type parameter has different bounds than the supertype's type parameter. Example (of a generic subtype with override-equivalent methods): class Super <T> { Let us change the subtype declaration slightly; we declare subclass methods so that they use the bound in the method signatures instead of the type parameter . Example (of a generic subtype with different yet override-equivalent methods): class Super <T> { Consider the example of a concrete parameterization of our super- and subtype in order to understand why it makes sense that the subtype version of the set method is an overriding rather than an overloading version of the supertype method, despite of the different signature. For illustration we use Sub<Integer> and its supertype Super<Integer> . The two methods involved in overriding are Super<Integer>.set(Integer) and Sub<Integer>.set(Number) . When a supertype reference refers to a subtype object and we invoke the set method through the supertype reference, then we see Super <Integer>.set( Integer ) , but actually invoke the overriding Sub <Integer>.set( Number ) . Is it type-safe? Example (of calling the overridden set method): Super<Integer> ref = new Sub<Integer>();
The opposite is true for the
get
methods. They have identical
signatures, but incompatible return types, namely
$T1_extends_Number
in the supertype and
Number
in the subtype. If we invoked the
subtype's
set
method via a supertype reference then we would expect
a return value of type
$T1_extends_Number
, which is a placeholder
for a subtype of
Number
, while in fact the subtype method would
return a
Number
reference, which can refer to any arbitrary subtype
of
Number
, not necessarily the one we are prepared to receive.
Hence considering the subtype version of the
get
method an overriding
version of the supertype's
get
method would not be type-safe and
is therefore rejected as an error.
In constrast, the following fairly similar example leads to an overloading situation. Example (of a generic subtype without override-equivalent methods): class Super <T> { To understand it let us use the same concrete parameterization as above, namely Sub<Integer> and its supertype Super<Number> . The two methods in question are Super<Number>.set(Number) and Sub<Integer>.set(Integer) . When a supertype reference refers to a subtype object and we invoke the set method through the supertype reference, then we see Super <Number>.set( Number ) , but would actually invoke Sub <Integer>.set( Integer ) , if this were overrding. This is not type-safe because we could pass an object of type Long to Super<Number>.set(Number) , but the subtype method Sub<Integer>.set(Integer) cannot take it. The get methods are not problematic in this example. They have the identical signatures and covariant return type, namely Number in the supertype and $T1_extends_Number in the subtype. Hence, we have overriding for the get methods. Overloading leads to confusing and/or ambiguous method invocations. Consider for example the instantiation Sub<Number> . In this instantiation we have Super<Number>.set(Number) in the supertype and overloaded version Sub<Number>.set(Number) in the subtype, both methods have the same signature. Example (of calling the overloaded set method): Integer integer = 10;
This is because the decision whether a subtype method overrides or overloads
a supertype method is made by the compiler when it compiles the generic
subtype. At that point in time there is no knowledge regarding the
concrete type by which the type parameter
N
might later be replaced.
Based on the declaration of the generic subtype the two
set
methods
have different signatures, namely
set(Number)
in the supertype
and
set($T1_extends_Number)
in the generic subtype. In a certain
instantiation of the subtype, namely in
Sub<Number>
, the type
parameter
N
(or
$T1_extends_Number
) might be replaced
by the concrete type
Number
. As a result both
set
methods of
Sub<Number>
suddenly have the same arguments type.
But that does not change the fact that the two methods still have different
signatures and therefore overload rather than override each other.
|
|
| LINK TO THIS | Technicalities.FAQ821 |
| REFERENCES |
What
is method overriding?
What is method overloading? What is the @Override annotation? What is a method signature? What is a subsignature? What are override-equivalent signatures? When does a method override its supertype's method? What are covariant-return types? What are substitutable return types? Can a method of a non-generic subtype override a method of a generic supertype? Why doesn't method overriding work as I expect it? |
| Yes. | |
|
This FAQ
entry discusses the relationship between generic methods in super- and
subtypes with respect to overriding and overloading.
Say, we have a supertype with generic methods and we intend to override the generic methods in a subtype. Which subtype methods are considered overriding versions of the the generic supertype methods? FAQ entry FAQ823 discusses which non-generic methods in a subtype override a generic method in a supertype. In this FAQ entry we explore which generic subtype methods override generic methods in a supertype. Example (of generic subtype methods overriding generic supertype methods): class Super {In this example the subtype methods are generic methods and have the same signatures as the supertype methods, namely <$T1_extends_Object>set($T1_extends_Object) and <$T1_extends_Object>get() . Note, that the names of the type parameters of the generic methods differ in the super- and the subtype. This, however, does not affect the signatures because the names of type parameters are irrelevant for the signature. If the methods have type parameters with different bounds , then they do not override, because the methods have signatures that are not override-equivalent. Remember, the type parameter bounds are part of a generic method's signature. Example (of generic subtype methods overloading generic supertype methods; not recommended): class Super {In this example the subtype methods have the signatures <$T1_extends_Number>set($T1_extends_Number) and <$T1_extends_Number>get() , while the supertype methods have the signatures <$T1_extends_Object>set($T1_extends_Object) and <$T1_extends_Object>get() . The signatures are clearly different and there is no chance that the subtype methods can ever override the supertype methods. The resulting overload situation makes for "interesting" effects when the methods are invoked; most of the time you will see the compiler complaining about ambiguous method calls. For this reason complex overloading situations like the one above are generally best avoided. Remember that the order of the type parameter bounds does not affect the signature of a generic method. For this reason, the methods in the following example do override, although they have different type parameter bounds. Example (of generic subtype methods overriding generic supertype methods): class Super { |
|
| LINK TO THIS | Technicalities.FAQ822 |
| REFERENCES |
What
is method overriding?
What is method overloading? What is the @Override annotation? What is a method signature? What is a subsignature? What is overload resolution? Can a generic method override a non-generic one? What is overload resolution? Why doesn't method overloading work as I expect it? |
| Yes. | |
|
This FAQ entry discusses the relationship between generic
and non-generic methods with respect to overloading.
Say, we have a supertype with generic methods and we intend to override the generic methods in a subtype. Which subtype methods are considered overriding versions of the the generic supertype methods? Before we discuss non-generic subtype methods that override generic supertype methods, let us consider the more obvious case of a subtype with generic methods that override generic methods from the supertype. Example (of generic subtype methods overriding generic supertype methods): class Super { Now, let us explore an example where non-generic subtype methods override generic supertype methods. Non-generic subtype methods are considered overriding versions of the generic supertype methods if the signatures' erasures are identical. Example (of non-generic subtype methods overriding generic supertype methods): class Super { There is one blemish in the case of the get method: we receive an unchecked warning because the return types are not really compatible. The return type of the subtype method get is Object , the return type of the supertype method get is an unbounded type parameter. The subtype method's return type and the supertype method's return type are not compatible , because the subtype method's return type is neither identical to the supertype method's return type nor is it a subtype thereof. However, the subtype method's return type Object is convertible to the supertype method's return type by means of an unchecked conversion. An unchecked warning indicates that a type check is necessary that neither the compiler nor the virtual machine can perform. In other words, the unchecked operation is not type-safe. In case of the convertible return types someone would have to make sure that the subtype method's return value is type-compatible to the supertype method's return type, but nobody except the programmer can ensure this.
The main purpose of allowing that methods with erased signatures override methods with generic signatures is backward compatibility. Imagine both classes had initially been non-generic legacy classes. In this initial situation both classes had had methods that take and return Object reference. Example (of legacy classes): class Super { Example (same as before, but after generification of the supertype): class Super {
At last, let us discuss a counter-example where the non-generic subtype methods do not override the generic supertype methods. Example (of non-generic subtype method overloading, instead of overriding, generic supertype methods; not recommended): class Super { The subtype method get on the other hand overrides the supertype method get . This is because the subtype method's signature is identical to the erasure of the supertype method's signature. The return types are not compatible, but only convertible by unchecked conversion; the subtype method's return type String is a subtype of the erasure of the supertype method's return type. That is, the subtype method's return type is acceptable as a result of the combination of unchecked conversion and covariance. The entire subclass is an example of poor design. The combination of overloading and overriding of corresponding methods is confusing at best. |
|
| LINK TO THIS | Technicalities.FAQ823 |
| REFERENCES |
What
is an unchecked warning?
What is method overriding? What is method overloading? What is the @Override annotation? What is a method signature? What is overload resolution? Can a generic method override a generic one? Can a generic method override a non-generic one? Why doesn't method overloading work as I expect it? |
| No. | |
|
This FAQ entry discusses whether a generic method in a
subtype can override an non-generic method inherited from a supertype.
We have seen in
FAQ823
that the converse is possible, namely that a regular, non-generic method
in a subtype can override a generic method inherited from a supertype.
It raises the question whether the same is true if the roles are flipped
and a generic method in a subtype attempts to override a non-generic method
from a supertype.
The answer is: No, generic methods in a subtype can not override non-generic methods in a supertype. Generic subtype methods can only overload non-generic supertype methods. Let us consider a superclass with non-generic methods and let us explore why generic methods in subtypes do not override the non-generic supertype methods. Example (of a supertype with non-generic methods and a subtype with generic methods): class Super { Let us consider a slightly different subtype, where again overloading is mixed up with overriding. Example (of a supertype with non-generic methods and a subtype with generic methods; not recommended): class Super { Example (of invoking the overloaded methods): Sub sub = new Sub(); The invocation through a subtype reference leads to a call to the subtype versions of the overloaded set method, provided the argument is of a subtype of Number, and to invocation of the supertype version of the method otherwise. Invocation of the overloaded get method through a subtype reference leads to an error message. Let us see how that happens. When the set method is invoked with a String argument, then the compiler finds only one viable method, namely the non-generic set(Object) method from the supertype. The generic subtype method is not a candidate for this invocation because String is no subtype of Number and hence the possible type argument String is not within bounds. Consequently, the supertype method is called. When the set method is invoked with a Integer argument, then the compiler finds two candidates for the invocation of the set method: the non-generic set(Object) method from the supertype and the parametrization <Integer>set(Integer) inferred from the generic subtype. Note, the compiler performs type argument inference before is looks for the best match among the candidate methods. Since the parametrization is the better match, the subtype method is invoked. For the invocation of the get method, the compiler finds two candidates: get() from the supertype and <$T1_extends_Number>get() from the subtype. Neither of the two signatures is more specific and thus the compiler reports an error. Note, the compiler does not consider the return type when it resolves the overloading and in particular does not perform type inference based on a generic method's return type. In our example it means that the compiler does not infer from the calling context that the instantiation <Integer>get() would be a viable candidate method and then picks it as the better match. Only the signature, and never the return type, of a method are relevant for overload resolution. The overload resolution is slightly different, when explicit instantiations are invoked. Example (of invoking the overloaded methods with explicitly specified type arguments): Sub sub = new Sub();In this situation the candidate set contains only the respective generic subtype method, because the supertype methods are not generic and for this reason cannot be invoked with explicitly specified type arguments. As the examples demonstrate, there is no way that a generic subtype method can override a non-generic supertype method. Instead, generic subtype methods overload non-generic supertype methods. Such overloading should generally be avoided because it is confusing and hard to understand. |
|
| LINK TO THIS | Technicalities.FAQ824 |
| REFERENCES |
What
is type argument inference?
What is method overriding? What is method overloading? What is the @Override annotation? What is a method signature? What is overload resolution? Can a generic method override a generic one? Can a non-generic method override a generic one? Can a generic method override a non-generic one? What is overload resolution? Why doesn't method overloading work as I expect it? |
| The process of determining the best match from a set of overloaded methods. | |
|
When a type has several overloaded methods, then the compiler
must decide which method to call when it finds a method invocation expression.
The process of picking the best match from the set of candidate methods
is called
overload-resolution
.
Example (of method overloading): class Super { Example (of overload resolution): Sub ref = new Sub(); When there is no exact match the compiler considers various conversions, among them the conversion from a subtype to a supertype (called reference-widening), conversions among primitive types (e.g. int to long ), autoboxing and unboxing (e.g. int to Integer ), and the conversion from a parameterized type to the raw type (called unchecked conversion). By and large, overload resolution is a complex process and can lead to surprising results, in the sense that the compiler picks and invokes a method that nobody had expected would be called. This sounds harmless, but can be a serious problem. For instance, when an overloading method is added to an existing class, then this additional candidate can change the result of overload resolution and thereby inadvertantly change the effect of method invocations in an unrelated part of the program. This is usually undesired and the resulting bugs are difficult to track down, because symptom and source of the problem are mostly unrelated. As a general rule, overloading should be used sparingly and judiciously. |
|
| LINK TO THIS | Technicalities.FAQ830 |
| REFERENCES |
What
is method overloading?
What is the @Override annotation? What is a method signature? What is a subsignature? What are override-equivalent signatures? Why doesn't method overriding work as I expect it? |