I was reading Effective Java chapter 5 about generics, in particular the items on preferring generic methods. I noticed that sometimes the type parameter(between angle brackets) in the method declaration before the return type is sometimes omitted. There are many cases like that, but for example on page 135 of the second edition:
public void popAll(Collection<E> dst) {
while (!isEmpty())
dst.add(pop());
}
On the other hand, I have seen similar generic methods with the declaration
public <E> void ...
Is the first one a typo? If not when can I omit the brackets from the declaration?
Thanks
E is a type variable -- it stands in for some other type, like String or Integer. So just as you can't understand dst.add(pop()) without knowing where and how dst was defined, you can't understand a method declaration like popAll(Collection<E> dst) without knowing where and how the type variable E is defined. In the case of popAll, the type variable E is defined at the class level: Stack<E>: it's the type of the elements in the stack. You'll often even see it javadoc'd:
/**A Stack of elements
*
*#param E The type of elements in the stack */
public class Stack<E>{
public void popAll(Collection<E> dst){ ... }
}
On the other hand, when you see a method declaration like public <E> void ..., the type variable E is being declared (not referenced from some enclosing scope like the enclosing class). In fact most times when you see a method with its own type variable, it's a static method, so there is no enclosing instance of the class to establish the value of E.
In both cases, what is the E type variable doing? It's telling us how two different types have to relate to each other. In popAll, it's telling us that the element type of the collection you want to put the popped elements into has to match the element type of the stack you're popping them from.
Similarly, take the example from page 136:
public class ListUtils{
public static <E> E reduce(List<E> list, Function<E> f, E initVal);
}
Here, the E type variable tells us that the element type of list has to match the argument type of f and the type of initVal. The surrounding class does not define E for us, it's only meaningful in the scope of the reduce method declaration.
The difference is that in the first case the whole class is declared generic whereas in the second case only the method is generic.
The answer is... it's not. A "generic method" is defined to be one that declares type variables before the return type:
A method is generic if it declares one or more type variables. (JLS 8.4.4)
Therefore, popAll is not a "generic method".
Since the method does not declare the type parameter E, it must be defined in an enclosing scope; almost certainly by the class (a "generic class") that contains the method.
Related
The code below doesn't compile on OpenjDK 11.
It seems to me that test1 in B should override test1 in A, because:
The methods have same name.
The methods have same parameter list.
The methods have same visibility.
The methods actually do not throw potentially incompatible checked exceptions.
Their return types are covariant.
I took a decompiler and decompiled each class separately. The compiled code actually worked as I would expect. It replaced U extends Number with Number, T extends Integer with Integer and so on. But when I try to compile the two classes together, I got and error on the second class that says the test in the second class does not override a method in the first one.
I'm missing something big or small here. And it's probably related to 5. Maybe the types are not covariant. Can you help me?
class A {
<U extends Number, T extends Number> U test(T test) {
System.out.println("In A.test(T test)");
return null;
}
//Decompiler shows that the above method erases to
// Number test(Number test)
// Just like the method below.
Number test2(Number test) {
return null;
}
}
class B extends A {
//Unsuccessful override. Compiler error.
#Override
<U extends Integer, T extends Number> U test(T test) {
System.out.println("In B.test(T tesT)");
return null;
}
//Decompiler shows that the above method erases to
//Integer test(Number test)
//Just like the method bellow.
//Successful override
#Override
Integer test2(Number test) {
return null;
}
}
The reason why your test methods produce an error is because they have totally unrelated, different signatures. Note that the signature of a method consists of its name, parameter list, and type parameters.
Quote from the Java Language Specification:
Two methods or constructors, M and N, have the same signature if they have the same name, the same type parameters (if any) (§8.4.4), and, after adapting the formal parameter types of N to the type parameters of M, the same formal parameter types.
Crucially, your two test methods do not have the same type parameters, because U in A.test has a different bound from U in B.test.
Two methods or constructors M and N have the same type parameters if both of the following are true:
M and N have same number of type parameters (possibly zero).
Where A1, ..., An are the type parameters of M and B1, ..., Bn are the type parameters of N, let θ=[B1:=A1, ..., Bn:=An]. Then, for all i (1 ≤ i ≤ n), the bound of Ai is the same type as θ applied to the bound of Bi.
Think about what would happen if B.test actually overrode A.test. You could pass a type to the type parameter U that is out of its bounds!
A a = new B();
// This will call B.test, and U is Double, T is Integer
// but U should extends Integer!
Double x = a.test((Integer)0);
For more info, here are the precise rules for when overriding happens. Note that criteria #4 and #5 on your list are not actually considered. They are just additional requirements that make your code not compile if you break them. One method is still "defined" to override another even if you break those requirements. They are listed here in the JLS.
Given that I find the relevant JLS rules (cited in Sweeper's answer above) quite confusing, it's useful to mention the rules, as formulated by the great people at Enthuware (https://enthuware.com/).
They don't mention the visibility and exceptions rules, but they go in detail with Generics. And they seem to explain my two cases, mentioned in the comments above. I'll just state the cases again here:
class A {
public <T,U> U test(T test) {
return null;
}
}
class B extends A {
//Successful override even though type parameters do not match
//This method even has none.
#Override
public Number test(Object test) {
return null;
}
}
class C {
public Object test(Object test) {
return null;
}
}
class D extends C {
//Same situation as A and B, but with places being exchanged.
//Now the generic method overrides non-generic.
//And we have error.
#Override
public <T,U> U test(T test) {
return null;
}
}
Now the steps, as formulated by Enthuware:
Steps to check for valid override:
First, check the method signature (i.e. method name and the parameter list). If the signature of the method in the subclass matches the signature of the method in the super class, then it could be a valid override, otherwise it is just an overloaded method. Note that signature does not include parameter names and parameter's generic type specification.
NOTE: I think they mean for us to replace the type parameter with its bound and then compare both methods.
Second, if it is a potential override, check the generic type specification of the parameters. If the overriding method does not use a generic type specification for the parameter type, then it is valid. The reverse is not valid i.e. the overriding method is allowed to erase the generic type specification but is not allowed to add a generic type specification if the overridden method does not have it. If both the methods have a generic type specification, then the specification must match exactly. For example, if the overridden method has Set<Integer>, then the overriding method can use Set or Set<Integer>. But if overridden method has Set, then the overriding method must also have Set for a valid override.
Third, if it is a potential override, check the return type. Java allows "covariant" returns, which means, the return type of the overriding method must be the same or be a subtype of the return type mentioned in the overridden method. Check the two return types without the generic type specification. If return type of the overriding method is covariant with respect to the return type of the overriding method (for example, ArrayList is covariant with List), then perform the same check including the generic type specification (for example, ArrayList<CharSequence> is covariant with List<? extends CharSequence>). Don't get confused by the presence of <T> in the code. The same rules of overriding still apply. The T in <T> is called as the "type" parameter. It is used as a place holder for whatever type is actually used while invoking the method. For example, if you call the method <T> List<T> transform(List<T> list) with List<String>, T will be typed to String. Thus, it will return List<String>. If, in another place, you call the same method with Integer, T will be typed to Integer and therefore, the return type of the method for that invocation will be List<Integer>
What does the first part in the following method definition?
<I, O> MyReturnType<I, O> myMethod() { ... }
The second is the method'sreturn type, third is the method name, but what is the first one?
I and O are declared as generic type parameters. They're generic types introduced by the method itself, as said here: https://docs.oracle.com/javase/tutorial/java/generics/methods.html
Generic methods are methods that introduce their own type parameters. This is similar to declaring a generic type, but the type parameter's scope is limited to the method where it is declared. Static and non-static generic methods are allowed, as well as generic class constructors.
If you don't declare <I, O>, java will look for types called I and O (which won't be there, since they're supposed to be generic).
I think #khelwood put it nicely (see comments on original question): It's saying: "In the following definition, I and O are standing in for some types that depend on the situation when the method is called."
I was reading about Set interface from here which the code sipped below which is a generic method that removes duplicates from a collection.
My question is what is that **< E>** placed after static before Set<E> ?
I mean wouldn't that Set<E> be enough ? why <E> was there twice?
public static <E> Set<E> removeDups(Collection<E> c) {
return new LinkedHashSet<E>(c);
}
Here **<E>** is a generic type. Generic type is defined as
A generic type is a generic class or interface that is parameterized
over types. The following Box class will be modified to demonstrate
the concept. LINK
And regarding you question related to <E>. A good description for it can be found on the same tutorial
Type Parameter Naming Conventions
By convention, type parameter names are single, uppercase letters.
This stands in sharp contrast to the variable naming conventions that
you already know about, and with good reason: Without this convention,
it would be difficult to tell the difference between a type variable
and an ordinary class or interface name.
The most commonly used type parameter names are:
E - Element (used extensively by the Java Collections Framework)
K - Key
N - Number
T - Type
V - Value
S,U,V etc. - 2nd, 3rd, 4th types
You'll see these names used throughout the Java SE API and the rest of
this lesson.
This means that this method declares a generic parameter type which the class does not define; in this case, you MUST declare the parameter type before the return type (even if this "return type" is void).
Think about it this way. Remove the initial <E>. Your declaration would then become:
public static Set<E> removeDups(Collection<E> c)
What is E here? Unless it is a generic parameter type defined by the class itself, it can only be an existing class.
Hence this syntax. It allows you to define a generic parameter for use in the method signature.
It's just the generic type used within the method. Static methods that use generic types must specify that type before the return type.
Reading the Java online tutorial I haven't understood anything about wildcard capture.
For example:
import java.util.List;
public class WildcardError {
void foo(List<?> i) {
i.set(0, i.get(0));
}
}
Why can't the compiler retain the assignment safely?
It knows that, by executing for instance, the method with an Integer List, it gets from i.get an Integer value. So it tries to set an Integer value at index 0 to the same Integer list (i).
So, what's wrong? Why write Wildcard helper?
why the compiler can't retain the assignment safe? It knows that,by executing for instance, the method with an Integer List, it gets from i.get an Integer value. So it try to set an Integer value at index 0 to the same Integer list (i).
Put differently, why does the compiler not know that the two usages of the wildcard type List<?> in
i.set(0, i.get(0));
refer to the same actual type?
Well, that would require the compiler to know that i contains the same instance for both evaluations of the expression. Since i isn't even final, the compiler would have to check whether i could possibly have been assigned in between evaluating the two expressions. Such an analysis is only simple for local variables (for who knows whether an invoked method will update a particular field of a particular object?). This is quite a bit of additional complexity in the compiler for rarely manifesting benefits. I suppose that's why the designers of the Java programming language kept things simple by specifying that different uses of the same wildcard type have different captures.
why the compiler can't retain the assignment safe?
The compiler doesn't know anything about the type of elements in List<?> i, by definition of ?. Wildcard does not mean "any type;" it means "some unknown type."
It knows that,by executing for instance, the method with an Integer List, it gets from i.get an Integer value.
That's true, but as I said above: the compiler can only know – at compile time, remember – that i.get(0) returns an Object, which is the upper bound of ?. But there's no guarantee that ? is at runtime Object, so there is no way for the compiler to know that i.set(0, i.get(0)) is a safe call. It's like writing this:
List<Foo> fooz = /* init */;
Object foo = fooz.get(0);
fooz.set(0, foo); // won't compile because foo is an object, not a Foo
More reading:
Can't add value to the Java collection with wildcard generic type
Java Collections using wildcard
Generic collection & wildcard in java
Generics - Cannot add to a List with unbounded wildcard
What is the difference betwen Collection<?> and Collection<T>
According to Get-Put principle:
If you have extends wildcard as in List<? extends Something>, then:
1A. You can get from the structure using Something or its superclass reference.
void foo(List<? extends Number> nums) {
Number number = nums.get(0);
Object number = nums.get(0); // superclass reference also works.
}
1B. You cannot add anything to the structure (except null).
void foo(List<? extends Number> nums) {
nums.add(1); Compile error
nums.add(1L); Compile error
nums.add(null); // only null is allowed.
}
Similarly, if you have super wildcard as in List<? super Something>, then:
2A. You can add to the structure which is Something or its subclass.
For eg:
void foo(List<? super Number> nums) {
nums.add(1); // Integer is a subclass of Number
nums.add(1L); // Long is a subclass of Number
nums.add("str"); // Compile error: String is not subclass of Number
}
2A. You cannot get from the structure (except via Object reference).
For eg:
void foo(List<? super Integer> nums) {
Integer num = nums.get(0); // Compile error
Number num = nums.get(0); // Compile error
Object num = nums.get(0); // Only get via Object reference is allowed.
}
Coming back to OP's question, List<?> i is just the short representation for List<? extends Object> i. And since it's a extends wildcard, the set operation fails.
The final piece remaining is WHY the operation fails ? Or why the Get-Put principle in the first place? - This has to do with type safety as answered by Jon Skeet here.
I also find this question hard to understand; splitting the single command into two commands helped me.
Below code is what actually happens in the background when the original method is inspected&compiled, the compiler makes its own local variable: the result of the i.get(0) call is placed in the register on the local variable stack.
And that is - for the understanding of this issue - the same as making a local variable which for convenience I have given the name element.
import java.util.List;
public class WildcardError {
void foo(List<?> i) {
Object element = i.get(0); // command 1
i.set(0, element); // command 2
}
}
When command 1 is inspected, it can only set the type of element to Object (--> upperbound concept, see Matt's answer), as it can not use ? as a variable type; the ? is only used for indicating that the generic type is unknown.
Variable types can only be real types or generic types, but since you don't use a generic type in this method like <T> for example, it is forced to use a real type. This forcing is done because of the following lines in the java specification (jls8, 18.2.1):
A constraint formula of the form ‹Expression → T› is reduced as follows:
[...]
– If the expression is a class instance creation expression or a method invocation expression, the constraint reduces to the bound set B3 which would be used to determine the expression's invocation type when targeting T, as defined in §18.5.2. (For a class instance creation expression, the corresponding "method" used for inference is defined in §15.9.3).
Solution Will Be,
import java.util.List;
public class WildcardError {
private void fooHelper(List<T> i){
i.set(0, i.get(0));
}
public void foo(List<?> i){
fooHelper(i);
}
}
here fooHelper will capture type T of wildcard ? (so as the name wildcard capture).
I think you misunderstand the ? in Generics. It isn't "wildcard capture" at runtime. It is "wildcard capture" at compile time. Since that capture is gone after compilation, the List<?> often becomes List<Object> or a list of some interface or common base class that is the lowest subclass that is shared amongst the instances according to the Generics type.
Because Java had to make the Generics system compatible with non-Generics supporting JVMs, there are no Generics types at runtime. This means that all checks / captures don't exist at runtime. Either the compiler permits a very narrow range of compatible Generics type assignments when it compiles, or it throws an error.
Now if you want to hold a Generics type at runtime, there are approaches. Most of them involve passing the Class object as a parameter into a constructor, which holds it in a field. Then you can refer to the field for the exact passed type. You can entangle this type with your Generics parameters; and by doing so, you can then have the actual type in places in the runtime code where the "Generics type parameters" have been replaced by their most permissive type supported in the real Java type system. I don't recommend doing this for many situations, as it is a lot of work (and not likely to give you much more security); but, occasionally you really do need the type.
I guess your misunderstanding of the restriction comes from substitution of ? for any type, Object or something like that in your mind. But that assumption is not correct, ? in fact means unknown type. So in the following line
fooz.set(0, foo);
you're trying to assign the variable of some type to the variable of unknown type (since the function signature is like void set(int, ?)), which can never be possible, whatever the type of foo would be. In your case the type of foo is Object and you cannot assign it to a variable of some unknown type, which in fact may be Foo, Bar or any other.
Please explain me why if I use the raw type A in the method test() , the get() method on my typed list returns an Object and not a B.:
public class test
{
public class B{}
public class C{}
public class A<T extends C>
{
private List<B> aBList;
public List<B> mGetBList()
{
return aBList;
}
}
public test(A pA) // Use of raw type - this is bad, I know!
{
B lB = pA.mGetBList().get(0); // Compile error: Type mismatch:
// cannot convert from Object to test.B
}
}
If I declare
public test(A<?> pA)
the get() method returns a B as expected.
+1 for interesting test case.
Looks like erasure erases everything, so in this case, you end up with.
public List mGetBList()
And erasure of List will result in public Object get( int ), which, of course, cannot be assigned to B.
If there is no strong need for raw type in method signature, use generic form that you have provided, otherwise cast the object to B
B lB = (B) pA.mGetBList().get(0);
"Doctor, it hurts when I do this."
Avoid raw types like the plague.
When you decalare your argument pa in the method test, no parameter is included, (including wildcard ). Therefore the list is held in a variable which contains a list of objects, so when you try to extract an element from the list you get a object, so you would have to cast it back to the required type, in this case B.
When using a wildcard the compliler is told not promote the list to a varibale containing an list holding Object. It is told that the contents are of the list are of 'unknown type' and is to be left alone. It is then upto the programmer to ensure that when extracting an element it is assigned to a suitable varibale, without using a cast.
Ive been doing some digging around ang found this.
http://java.sun.com/docs/books/tutorial/extra/generics/legacy.html
Basically, erasure gets rid of (or erases) all generic type information. All the type information betweeen angle brackets is thrown out, so, for example, a parameterized type like List is converted into List. All remaining uses of type variables are replaced by the upper bound of the type variable (usually Object).
By upper bound im taking it if they mean < T extends C>, C would be the upper bound, then as only B is the type varibable for aBlist then its upper bound would be Object.
Anway hope this helps (Please dont mark me down again).