|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
HOME | SEMINARS | TALKS | ARTICLES | BOOKS | LINKS | IOSTREAMS | GENERICS | ABOUT | NEWSLETTER | CONTACT | SITEMAP | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Java Generics FAQs - Programming With Java Generics
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
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.
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Practicalities - Programming With Java Generics© Copyright 2004-2007 by Angelika Langer. All Rights Reserved. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Yes, using parameterized types has various advantages and is recommended, unless you have a compelling reason to prefer the raw type. | |
|
It is permitted to use generic types without type arguments,
that is, in their raw form. In principle, you can entirely ignore Java
Generics and use raw types throughout your programs. It is, however,
recommended that type arguments are provided when a generic type is used,
unless there is a compelling reason not to do so.
Providing the type arguments rather than using the raw type has a couple of advantages:
|
|
| LINK TO THIS | #FAQ001 |
| REFERENCES |
How
does the compiler translate Java generics?
What is an "unchecked" warning? What is the benefit of using Java generics? |
Why
shouldn't I mix parameterized and raw types, if I feel like it?
| Because it is poor style and highly confusing to readers of your source code. | |
|
Despite of the benefits of parameterized types you might
still prefer use of raw types over using pre-defined generic types in their
parameterized form, perhaps because the raw types look more familiar. To
some extent it is a matter of style and taste and both styles are permitted.
No matter what your preferences are: be consistent and stick to it.
Either ignore Java generics and use raw type in all places, or take advantage
of the improved type-safety and provide type arguments in all places. Mixing
both styles is confusing and results in "unchecked" warnings that can and
should be avoided.
Naturally, you have to mix both styles when you interface with source code that was written before the advent of Java generics. In these cases you cannot avoid the mix and the inevitable "unchecked" warnings. However, one should never have any "unchecked" warnings in code that is written in generic style and does not interface with non-generic APIs. Here is a typical beginner's mistake for illustration. Example (of poor programming style): List <String> list = new ArrayList <String> ();Beginners often start out correctly providing type arguments and suddenly forget, in the heat of the fighting, that methods of parameterized types often return other parameterized types. This way they end up with a mix of generic and non-generic programming style, where there is no need for it. Avoid mistakes like this and provide type arguments in all places. Example (corrected): List <String> list = new ArrayList <String> ();Here is an example of a code snippet that produces avoidable "unchecked" warnings. Example (of avoidable "unchecked" warning): void f(Object obj) {The getClass method returns an instantiation of class Class , namely Class<? extends X> , where X is the erasure of the static type of the expression on which getClass is called. In the example, the parameterization of the return type is ignored and the raw type Class is used instead. As a result, certain method calls, such as the invocation of getAnnotation , are flagged with an "unchecked" warning. In general, it is recommended that type arguments are provided unless there is a compelling reason not to do so. In case of doubt, often the unbounded wildcard parameterized type is the best alternative to the raw type. It is sematically equivalent, eliminates "unchecked" warnings and yields to error messages if their use is unsafe. Example (corrected): void f(Object obj) { |
|
| LINK TO THIS | #FAQ002 |
| REFERENCES |
What
is the benefit of using Java generics?
What does type-safety mean? What is an "unchecked" warning? What is the raw type? What is a parameterized or generic)type? How is a generic type instantiated? What is an unbounded wildcard parameterized type? |
Should
I use the generic collections or stick to the old non-generic collections?
| Provide type arguments when you use collections; it improves clarity and expressiveness of your source code. | |
|
The JDK collection framework has been re-engineered. All
collections are generic types since Java 5.0. In principle, you can
choose whether you want to use the pre-defined generic collections in their
parameterized or raw form. Both is permitted, but use of the parameterized
form is recommended because it improves the readability of your source
code.
Let us compare the generic and non-generic programming style and see how they differ. Example (of non-generic style):
final class HtmlProcessor {
From the code snippet above it is relatively difficult to tell what the various collections contain. This is typical for non-generic code. The raw type collections do not carry information regarding their elements. This lack of type information also requires that we cast to the alledged element type each time an element is retrieved from any of the collections. Each of these casts can potentially fail at runtime with a ClassCastException . ClassCastException s are a phenomenon typical to non-generic code. If we translate this non-generic source code with a Java 5.0 compiler, we receive "unchecked" warnings when we invoke certain operations on the raw type collections. We would certainly ignore all these warnings, or suppress them with the SuppressWarnings annotation. Example (of generic counterpart):
final class HtmlProcessor {
From the generic source code we can easily tell what type of elements are stored in the various collections. This is one of the benefits of generic Java: the source code is substantially more expressive and captures more of the programmer's intent. In addition it enables the compiler to perform lots of type checks at compile time that would otherwise be performed at runtime. Note that we got rid of all casts. As a consequence there will be no runtime failure due to a ClassCastException .
This is a general rule in Java 5.0: if your source code compiled
without any warnings then there will be no unexpected
ClassCastException
s
at runtime. Of course, if your code contains explicit cast expressions
any exceptions resulting from these casts are not considered unexpected.
But the number of casts in your source code will drop substantially with
the use of generics.
|
|
| LINK TO THIS | #FAQ003 |
| REFERENCES |
package
java.util
Should I prefer parameterized types over raw types? What is the benefit of using Java generics? What is an "unchecked" warning? How can I disable or enable unchecked warnings? What is the SuppressWarnings annotation? What is the raw type? What is a parameterized or generic type? How is a generic type instantiated? |
| A view to a regular collection that performs a runtime type check each time an element is inserted. | |
|
Despite of all the type checks that the compiler performs
based on type arguments in order to ensure type safety it is still possible
to smuggle elements of the wrong type into a generic collection.
This can happen easily when generic and non-generic code is mixed.
Example (of smuggling an alien into a collection): class Legacy {An "alien" Date object is successfully inserted into a list of strings. This can happen inadvertantly when a parameterized type is passed to a piece of legacy code that accepts the corresponding raw type and then adds alien elements. The compiler can neither detect nor prevent this kind of violation of the type safety, beyond issuing an "unchecked" warning when certain methods of the raw type are invoked. The inevitable type mismatch will later show up in a potentially unrelated part of the program and will mainfest itself as an unexpected ClassCastException . For purposes of diagnostics and debugging JDK 5.0 adds a set of “checked” views to the collection framework (see java.util.Collections ), which can detect the kind of problem explained above. If a checked view is used instead of the original collection then the error is reported at the correct location, namely when the "alien" element is inserted. Example (of using a checked collection): class Legacy {The checked collection is a view to an underlying collection, similar to the unmodifiable and synchronized views provided by class Collections . The purpose of the checked view is to detect insertion of "aliens" and prevent it by throwing a ClassCastException in case the element to be inserted is of an unexptected type. The expected type of the elements is provided by means of a Class object when the checked view is created. Each time an element is added to the checked collection a runtime type check is performed to make sure that element is of an acceptable type. Here is a snippet of the implementation of the checked view for illustration. Example (excerpt from a checked view implementation): public class Collections {The advantage of using a checked view is that the error is reported at the correct location. The downside of using a checked collection is the performance overhead of an additional dynamic type check each time an element is inserted into the collection. The error detection capabilities of the checked view are somewhat limited. The type check that is performed when an element is inserted into a checked collection is performed at runtime - using the runtime type representation of the expected element type. If the element type is a parameterized type the check cannot be exact, because only the raw type is available at runtime. As a result, aliens can be inserted into a checked collection, although the checked collection was invented to prevent exactly that. Example (of limitations of checked collections): class Legacy {The checked view can only check against the raw type Pair and cannot prevent that an alien pair of type Pair<Date,String> is inserted into the checked view to a collection of Pair<String,String> . Remember, parameterized types do not have an exact runtime type representation and there is not class literal for a parameterized type that we could provide for creation of the checked view. Note, that a checked view to a collection of type Pair<String,String> cannot be created without a warning. Example: Lis t<Pair<String,String>> stringPairsWe cannot create a checked view to a parameterized type such as List<Pair<String,String>> , because it is required that we supply the runtime type representation of the collection's element type as the second argument to the factory method Collections.checkedList . The element type Pair<String,String> does not have a runtime type representation of its own; there is no such thing as Pair<String,String>.class . At best, we can specify the raw type Pair as the runtime type representation of the collection's element type. But this is the element type of a collection of type List<Pair> , not of a List<Pair<String,String>> . This explains why we have to add a cast. The natural cast would be to type List<Pair> , but the conversion from ArrayList<Pair<String,String>> to List<Pair> is not permitted. These two types a inconvertible because they are instantiations of the same generic type for different type arguments. As a workaround we resort to the raw type List , because the conversion ArrayList<Pair<String,String>> to List is permitted for reasons of compatibility. Use of the raw type results in the usual "unchecked" warnings. In this case the compiler complains that we pass a raw type List as the first arguments to the Collections.checkedList method, where actually a List<Pair> is exptected. In general, we cannot create a checked view to an instantiation of a collection whose type argument is a parameterized type (such as List<Pair<String,String>> ). This is only possible using debatable casts, as demonstrated above. However, it is likely that checked collections are used in cases where generic and non-generic legacy code is mixed, because that is the situation in which alien elements can be inserted into a collection inadvertantly. In a mixed style context, you might not even notice that you work around some of the compiler's type checks, when you create a checked view, because you have to cope with countless "unchecked" warnings anyway. The point to take home is that checked views provide a certain safety net for collections whose element type is a raw type, but fails to provide the same kind of safety for collections whose element type is a parameterized type. |
|
| LINK TO THIS | #FAQ004 |
| REFERENCES |
class
java.util.Collections
What is an "unchecked" warning? What is the raw type? What happens when I mix generic and non-generic code? How do I pass type information to a method so that it can be used at runtime? How do I perform a runtime type check whose target type is a type parameter? Why is there no class literal for concrete parameterized types? How does the compiler translate Java generics? What is type erasure? What is the type erasure of a parameterized type? |
| Collection<Object> is a heterogenous collection, while Collection<?> is a homogenous collection of elements of the same unknown type. | |
|
The type
Collection<Object>
is a
heterogenous
collection of objects of different types. It's a mixed bag and can
contain elements of all reference types.
The type Collection<?> stands for a representative from the family of types that are instantiations of the generic interface Collection , where the type argument is an arbitrary reference type. For instance, it refers to a Collection<Date> , or a Collection<String> , or a Collection<Number> , or even a Collection<Object> .
A
Collection<?>
is a
homogenous
collection in the
sense that it can only contain elements that have a common unknown supertype,
and that unknown supertype might be more restrictive than
Object
.
If the unknown supertype is a
final
class then the collection
is truly homogenous. Otherwise, the collection is not really homogenous
because it can contain objects of different types, but all these types
are subtypes of the unknown supertype. For instance, the
Collection<?>
might
stand for
Collection<Number>
, which is homogenous in the sense
that it contains numbers and not apples or pears, yet it can contain a
mix of elements of type
Short
,
Integer
,
Long
,
etc.
A similar distinction applies to bounded wildcards, not just the unbounded wildcard " ? ". A List<Iterable> is a concrete parameterized type. It is a mixed list of objects whose type is a subtype of Iterable . I can contain an ArrayList and a TreeSet and a SynchronousQueue , and so on. A List<? extends Iterable> is a wildcard parameterized type and stands for a representative from the family of types that are instantiations of the generic interface List , where the type argument is a subtype of Iterable , or Iterable itself. Again, the list is truly homogenous if the unknown subtype of Iterable is a final class. Otherwise, it is a mix of objects with a common unknown supertype and that supertype itself is a subtype of Iterable . For example, List<? extends Iterable> might stand for List<Set> , which is homogenous in the sense that it contains sets and not lists or queues. Yet the List<Set> can be heterogenous because it might contain a mix of TreeSet s and HashSet s. |
|
| LINK TO THIS | #FAQ005 |
| REFERENCES |
What
is a concrete parameterized type?
What is a wildcard parameterized type? |
| Using wildcard instantiations of the generic collections. | |||||||||||||||||||||
|
Occasionally, we want to refer to sequences of objects
of different types. An example would be a
List<Object>
or a
Object[]
.
Both denote sequences of objects of arbitrary types, because
Object
is the supertype of all reference types.
How do we express a sequence of objects not of arbitrary different types,
but of different instantiations of a certain generic type? Say, we need
to refer to a sequence of pairs of arbitrary elements. We would need the
supertype of all instantiations of the generic
Pair
type. This
supertype is the unbounded wildcard instantiation
Pair<?,?>
.
Hence a
List<Pair<?,?>>
and a
Pair<?,?>[]
would
denote sequences of pairs of different types.
When we want to refer to a mixed sequence of certain types, instead of all arbitrary types, we use the supertype of those "certain types" to express the mixed sequence. Examples are List<Number> or Number[]. The corresponding mixed sequences of instantiations of a generic type is expressed in a similar way. A mixed sequences of pairs of numbers can be expressed as List<Pair<? extends Number, ? extends Number>> or as Pair<? extends Number, ? extends Number>[] .
Example (of illegal array creation): Pair<? extends Number, ? extends Number>[] makeNumberPairs(int size) {By and large an array type such as Pair<? extends Number, ? extends Number>[] is not particularly useful, because it cannot refer to an array of its type. It can refer to an array of the corresponding raw type, i.e. Pair[], or to an array of a non-generic subtype, e.g. Point[] , where Point is a subclass of Pair<Double,Double> for instance. In each of these cases using a reference variable of type Pair<? extends Number, ? extends Number>[] offers no advantage over using a reference variable that matches the type of the array being refered to. Quite the converse; it is error prone and should be avoided. This rules applies to all array types with a component type that is a concrete or bounded wildcard parameterized type. For details see ParameterizedTypes.FAQ104A and ParameterizedTypes.FAQ307A . Note that arrays of unbounded wildcard parameterized types do not suffer from this restriction. The creation of an array of an unbounded wildcard parameterized type is permitted, because the unbounded wildcard parameterized type is a so-called reifiable type, so that an array reference variable with an unbounded wildcard parameterized type as its component type, such as Pair<?,?>[] , can refer to an array of its type. Example (of legal array creation): Pair<?,?>[] makeNumberPairs(int size) { |
|||||||||||||||||||||
| LINK TO THIS | #FAQ006 | ||||||||||||||||||||
| REFERENCES |
Can
I create an object whose type is a wildcard 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? Can I declare a reference variable of an array type whose component type is a concrete parameterized type? Can I declare a reference variable of an array type whose component type is a bounded wildcard parameterized type? Can I declare a reference variable of an array type whose component type is an unbounded wildcard parameterized type? What is a reifiable type? |
||||||||||||||||||||
| All three types refer to collections that hold pairs where the first part is a String and the second part is of an arbitrary type. The differences are subtle. | |
|
The three parameterized types are relatively similar.
They all refer to collections that hold pairs where the first part is a
String
and the second part is of an arbitrary type.
Let us start with a comparison of the two concrete parameterized types Collection<Pair<String,Object>> and Collection<Pair<String,?>> . The both contain pairs where the first part is a String . The individual pairs stored in the collection can for instance contain a String and a Date , or a String and an Object , or a String and a String . The difference lies in the types of the pairs that can be added to the two collections. Example (using a Collection<Pair<String,Object>> ): Collection< Pair<String, Object > > c = new ArrayList<Pair<String,Object>>();The example demonstrates that only pairs of type Pair<String,Object> can be added to a Collection<Pair<String,Object>> . A Collection<Pair<String,Object>> is a homogenous collections of elements of the same type. The individual pairs may contain different things, as long as the type of the pair is Pair<String,Object> . For instance, a pair may consist of a String and a Date , but it must not be of type Pair<String,Date> . Example (using a Collection<Pair<String,?>> ): Collection< Pair<String, ? > > c = new ArrayList<Pair<String,?>>();The example illustrates that a Collection<Pair<String,?>> accepts all types of pairs as long as the first type argument is String . For instance, a pair of type Pair<String,Date> is accepted. A Collection<Pair<String,?>> is a heterogenous collections of elements of the similar types.
The key difference between a
Collection<Pair<String,Object>>
and
a
Collection<Pair<String,?>>
is that the first contains
elements of the same type and the latter contains elements of different
similar types.
The type Collection<? extends Pair<String,?>> is fundamentally different. It is a wildcard parameterized type, not a concrete parameterized type. We simply do not know what exactly a reference variable of the wildcard type refers to. Example (using a Collection<? extends Pair<String,?>> ): Collection< ? extends Pair<String, ? > > c = new ArrayList<Pair<String,?>>();The type Collection<? extends Pair<String,?>> stands for a representative from the family of all instantiations of the generic type Collection where the type argument is a subtype of type Pair<String,?> . This type family includes members such as Pair<String,String> , Pair<String,Object> , Pair<String,? extends Number> , and Pair<String,?> itself .
Methods like
add
must not be invoked through a reference of
a wildcard type. This is because the
add
method takes an argument
of the unknown type that the wildcard stands for. Using the variable
c
of the wildcard type
Collection<? extends Pair<String,?>>
,
nothing can be added to the collection. This does not mean that the
collection being refered to does not contain anything. We just do
not know what exactly the type if the collection is and consequently we
do not know what type of elements it contains. All we know is that
is contains pairs where the first part is a
String
. But
we do not know of which type the second part of the pair is, or whether
or not all pairs are of the same type.
So far, we've silently assumed that Pair is a final class. What if it has subtypes? Say, it has a subtype class SubTypeOfPair<X,Y> extends Pair<X,Y> . In that case, a Collection<Pair<String,Object>> may not only contain objects of type Pair<String,Object> , but also objects of type SubTypeOfPair<String,Object> . A Collection<Pair<String,?>> may not only contain objects of different pair types such as Pair<String,Date> and Pair<String,Object> , but also objects of subtypes of those, such as SubTypeOfPair<String,Date> and SubTypeOfPair<String,Object> .
The type
Collection<? extends Pair<String,?>>
stands
for a representative from the family of all instantiations of the generic
type
Collection
where the type argument is a subtype of type
Pair<String,?>
.
This type family is now even larger. It does not only include members
such as
Pair<String,String>
,
Pair<String,Object>
,
Pair<String,?
extends Number>
, and
Pair<String,?>
itself, but also type
such as
SubTypeOfPair<String,String>
,
SubTypeOfPair<String,Object>
,
SubTypeOfPair<String,?
extends Number>
, and
SubTypeOfPair<String,?>
.
|
|
| LINK TO THIS | #FAQ006A |
| REFERENCES |
What
is a bounded wildcard?
Which methods and fields are accessible/inaccessible through a reference variable of a wildcard type? What is the difference between a Collection<?> and a Collection<Object>? Which super-subset relationships exist among wildcards? |
| In general you can't. | |
|
If the same wildcard appears repeatedly, each occurrence
of the wildcard stands for a potentially different type. There is no way
to make sure that the same wildcard represents the same type.
Example (using the same wildcard repeatedly): Pair< ? , ? > couple = new Pair< String , String >("Orpheus","Eurydike");There is nothing you can do to make sure that a reference variable of type Pair<?,?> represents a pair of elements of the same type. Depending on the circumstances there might be work-arounds that achieve this goal. For instance, if the type Pair<?,?> is the type of a method argument, it might be possible to generify the method to ensure that the method argument is a pair of elements of the same type. For instance, the following method void someMethod(Pair< ? , ? > pair) { ... }accepts all types of pairs. It is mostly equivalent to the following generic method: < X , Y > void someMethod(Pair< X , Y > pair) { ... }In order to make sure that only pairs of elements of the same type are passed to the method, the method can be generified as follows: < T > void someMethod(Pair< T , T > pair) { ... }Now it is guaranteed that the method accepts only pairs of elements of the same type. |
|
| LINK TO THIS | #FAQ007 |
| REFERENCES |
What
is a wildcard parameterized type?
If a wildcard appears repeatedly in a type argument section, does it stand for the same type? |
Why
doesn't method overloading work as I expect it?
| Because there is only one byte code representation of each generic type or method. | |
|
When
you invoke an overloaded method and pass an argument to the method whose
type is a type variable or involves a type variable, you might observe
surprising results. Let us study an example.
Example (of invocation of an overloaded method): static void overloadedMethod( Object o) {We have several overloaded versions of a method. The overloaded method is invoked by a generic method which passes an argument of type T to the overloaded method. Eventually the generic method is called and a string is passed as an argument to the generic method. One might expect that inside the generic method the string version of the overloaded method is invoked, because the method argument is a string. This, however, is wrong.
The program prints:
overloadedMethod(
Object
)
called
In our example the generic method is translated to the following representation:
void genericMethod(
Object
t)
{
overloadedMethod (t) ; }
More generally speaking: overload resolution happens at compile time, that is, the compiler decides which overloaded version must be called. The compiler does so when the generic method is translated to its unique byte code representation. During that translation type erasure is performed, which means that type parameters are replaced by their leftmost bound or Object if no bound was specified. Consequently, the leftmost bound or Object determines which version of an overloaded method is invoked. What type of object is passed to the method at runtime is entirely irrelevant for overload resolution.
Here is another even more puzzling example.
Example (of invocation of an overloaded method):
public
final class GenericClass<T> {
private void overloadedMethod( Collection<?> o) { System.out.println(" overloadedMethod (Collection<?>)"); } private void overloadedMethod( List<Number> s) { System.out.println(" overloadedMethod (List<Number>)"); } private void overloadedMethod( ArrayList<Integer> i) { System.out.println(" overloadedMethod (ArrayList<Integer>)"); } private void method(List<T> t) { overloadedMethod(t); // which method is called? }
public static
void main(String[] args) {
overloadedMethod
(Collection<?>)
One might have expected that version for
ArrayList<Integer>
would be invoked, but that again is the wrong expectation. Let us
see what the compiler translates the generic class to.
Example (after type erasure):
public
final class GenericClass {
One might mistakenly believe that the compiler would decide that the
List
version of the overloaded method is the best match. But that would
be wrong, of course. The
List
version of the overloaded method was originally a version that takes a
List<Number>
as an argument, but on invocation a
List<T>
is passed, where
T
can
be any type and need not be a
Number
.
Since
T
can be any type
the only viable version of the overloaded method is the version for
Collection<?>
.
private void overloadedMethod( Collection o) { System.out.println(" overloadedMethod (Collection<?>)"); } private void overloadedMethod( List s) { System.out.println(" overloadedMethod (List<Number>)"); } private void overloadedMethod( ArrayList i) { System.out.println(" overloadedMethod (ArrayList<Integer>)"); } private void method(List t) { overloadedMethod(t); }
public static
void main(String[] args) {
Conclusion: Avoid passing type variables to overloaded methods. Or, more precisely, be careful when you pass an argument to an overloaded method whose type is a type variable or involves a type variable. |
|
| LINK TO THIS | #FAQ050 |
| REFERENCES |
How
does the compiler translate Java generics?
What is type erasure? What is method overriding? What is method overloading? What is a method signature? What is the @Override annotation? What are override-equivalent signatures? When does a method override its supertype's method? What is overload resolution? |
Why
doesn't method overriding work as I expect it?
| Because the decision regarding overriding vs. overloading is based on the generic type, not on any instantiation thereof. | |
|
Sometimes,
when you believe you override a method inherited from a supertype you inadvertantly
overload instead of override the inherited method. This can lead
to surprising effects. Let us study an example.
Example (of overloading):
class
Box
<T>
{
private T theThing; public Box( T t) { theThing = t; } public void reset( T t) { theThing = t; } ... } class WordBox< S extends CharSequence > extends Box< String > { public WordBox( S t) { super(t.toString().toLowerCase()); } public void reset( S t) { super.reset(t.toString().toLowerCase()); } ... } class Test { public static void main(String[] args) { WordBox<String> city = new WordBox<String>("Skogland"); city.reset("Stavanger"); // error: ambiguous } } error: reference to reset is ambiguous, both method reset (T) in Box<String> and method reset(T) in WordBox<String> match city.reset("Stavanger"); ^ The problem is that the subclass's reset method does not override the superclass's reset method, but overloads it instead. You can easily verify this by using the @Override annotation.
Example (of overloading):
class
Box
<T>
{
private T theThing; public Box( T t) { theThing = t; } public void reset( T t) { theThing = t; } ... } class WordB ox <S extends CharSequence> extends Box <String> { public WordBox( S t) { super(t.toString().toLowerCase()); } @Override public void reset( S t) { super.reset(t.toString().toLowerCase()); } ... } error: method does not override a method from its superclass @Override ^
The overloading happens because the two methods have different signatures. This might come as a surprise, especially in the case of the instantation WordBox<String> , where the two reset methods have the same name and the same parameter type. The point is that the compiler decides whether a subtype method overrides or overloads a supertype method when it compiles the generic subtype, independently of any instantiations of the generic subtype. When the compiler compiles the declaration of the generic WordBox<S extends CharSequence> class, then there is no knowledge regarding the concrete type by which the type parameter S might later be replaced. Based on the declaration of the generic subtype the two reset methods have different signatures, namely reset(String) in the supertype and reset(S_extends_CharSequence) in the generic subtype. These are two completely different signatures that are not override-equivalent. Hence the compiler considers them overloading versions of each other. In a certain instantiation of the subtype, namely in WordBox<String> , the type parameter S might be replaced by the concrete type String. As a result both reset methods visible in WordBox<String> suddenly have the same argument type. But that does not change the fact that the two methods still have different signatures and therefore overload rather than override each other. The identical signatures of the two overloading version of the reset method that are visible in WordBox<String> lead to the anbiguitiy that we observe in our example. When the reset method is invoked through a reference of type WordBox<String> , then the compiler finds both overloading versions. Both versions are perfect matches, but neither is better than the other, and the compiler rightly reports an ambiguous method call.
Conclusion: Be careful when you override methods, especially when generic types or generic methods are involved. Sometimes the intended overriding turns out to be considered overloading by the compiler, which leads to surprising and often confusing results. In case of doubt, use the @Override annotation. |
|
| LINK TO THIS | #FAQ051 |
| REFERENCES |
How
does the compiler translate Java generics?
What is type erasure? What is method overriding? What is method overloading? What is a method signature? What is the @Override annotation? When does a method override its supertype's method? Can a method of a generic subtype override a method of a generic supertype? |
What
happens when I mix generic and non-generic legacy code?
| The compiler issues lots of "unchecked" warnings. | |
|
It is permitted that a generic class or method is used
in both its parameterized and its raw form. Both forms can be mixed
freely. However, all uses that potentially violate the type-safety
are reported by means of an "unchecked warning". In practice, you
will see a lot of unchecked warnings when you use generic types and methods
in their raw form.
Example (of mixing paramterized and raw use of a generic type): interface Comparable<T> {The Comparable interface is a generic type. Its raw use in the example above leads to "unchecked" warnings each time the compareTo method is invoked. The warning is issued because the method invocation is considered a potential violation of the type-safety guarantee. This particular invocation of compareTo is not unsafe, but other methods invoked on raw types might be. Example (of type-safety problem when mixing parameterized and raw use): class Test {Similar to the previous example, the invocation of the add method on the raw type List is flagged with an "unchecked" warning. The invocation is indeed unsafe, because it inserts a string into a list of long values. The compiler cannot distinguish between invocations that are safe and those that are not. It reports "unchecked" warnings just in case that a call might be unsafe. It applies a simple rule: every invocation of a method of a raw type that takes an argument of the unknown type that the class's type parameter stands for, is potentially unsafe. That does not mean, it must be unsafe (see Comparable.compareTo ), but it can be unsafe (see List.add ). If you find that you must intermix legacy and generic code, pay close attention to the unchecked warnings. Think carefully how you can justify the safety of the code that gives rise to the warning. Once you've made sure the warning is harmless suppress it using the SuppressWarnings annotation. If you can re-engineer existing code or if you write new code from scratch you should use generic types and methods in their parmeterized form and avoid any raw use. For instance, the examples above can be "repaired" as follows: Example #1 (corrected): interface Comparable<T> {No "unchecked" warning occurs if the Comparable interface is used in its parameterized form in all places. Example #2 (corrected): class Test {The "unchecked" warning in someMethod is no longer necessary if the generic type List is used in its parameterized form as List<String> . With this additional type information the compiler is now capable of flagging the formerly undetected type-safety problem in method test as an error. |
|
| LINK TO THIS | #FAQ101 |
| REFERENCES |
What
does type-safety mean?
What is the raw type? Can I use a raw type like any other type? What is an "unchecked" warning? How can I disable or enable unchecked warnings? What is the SuppressWarnings annotation? |
| No, most likely not. | |
|
Not all types are inherently generic. There is no
point to turning a type into a generic type if the type does not semantically
depend on a particular unknown type that can be more adequately be expressed
by means of a type parameter.
Example (of an arbitrary non-generic type taken from package org.w3c.dom ): public interface NameList {The NameList interface takes and returns either strings or primitive types and there is no reason why this class should be generic in any form. Other non-generic types would benefit from generics. Example (of another arbitrary non-generic type): public interface Future {This interface has get methods that return Object references. If these methods return the same type of object for a given instance of type Future , then the interface is more precisely declared as a generic interface. Example (of corresponding generic type): public interface Future <V> { Occasionally, the generification of one type leads to the generification of other related types. Example (of non-generic types taken from package java.lang.ref in JDK 1.4): public class ReferenceQueue {The abstract class Reference internally holds a reference of type Object and has methods that take and return Object references. If these methods take and return the same type of object that is held internally, then the class is more precisely declared as a generic class, namely as Reference<T> where T is the type of the referent. When we decide to parameterize class Reference then we must provide type arguments in all places where type Reference is used. This affects class ReferenceQueue because it has methods that return references of type Reference . Consequently, we would declare class ReferenceQueue as a generic class, too. Once we have generified class ReferenceQueue then we must return to class Reference and provide type arguments in all places where type ReferenceQueue is used. Example (of corresponding generic type in JDK 5.0): public class ReferenceQueue <T |