How can a similar functionality be achieved without errors?
class A<K> {
void f(K x) {}
}
void foo(A<? extends X> a, X x) {
a.f(x); // AN error: The method f(capture#1-of ? extends X) in the
// type A<capture#1-of ? extends X> is not applicable for the
// arguments (X)
}
I know that it happens because 'a' can be an instance of A<"non-X">, so its 'f' mustn't accept an instance of X as a parameter, but how can I force the parameters to be of the same type?
Here is more code:
Test class:
class Test {
<T> void foo(A<T> a, T x) {
a.f(x); // now it works!
}
}
In some class:
Container<X> container;
public void test() {
X x = new X();
new Test().foo(container.get(), x);
}
Here's the container class:
public class Container<K> {
A<? extends K> get() {
return new A<K>();
}
}
Whenever you want to restrict the type parameter to subtypes of a particular class you can use the bounded type parameter. If you just specify a type (class) as bounded parameter, only sub types of that particular class are accepted by the current generic class.
Static and non-static generic methods are allowed, as well as generic class constructors. The syntax for a generic method includes a list of type parameters, inside angle brackets, which appears before the method's return type.
A generic method can have one or more type parameters, such as the "T" in countOccurrences.
Multiple parametersYou can also use more than one type parameter in generics in Java, you just need to pass specify another type parameter in the angle brackets separated by comma.
You can force the parameters to be of the same type by doing the following:
// the first class, A<K>:
class A<K> {
void f(K x) {}
}
// the second class, defining the method with generic type parameters
class Test {
<T> void foo(A<T> a, T x) {
a.f(x); // now it works!
}
}
// a third class, that uses the above two:
class Main {
public static void main(final String... args) {
final Test test = new Test();
final A<String> a = new A<>();
test.foo(a, "bar");
}
}
What this does is: the method foo
defines a generic type parameter T
and uses it to enforce that the K
type parameter of the class A
must match the type of x
, the second parameter of foo
.
You could even impose restrictions on <T>
if you wish and if it makes sense for your problem, such as <T extends Bar> void foo(A<T> a, T x) {...}
, or with super
. You would want this if, as Joni asked on a comment in the question, X
is actually a type and not a type parameter: you'd use <T extends X> void foo(...)
.
After you've shown more code, the problem becomes clear.
The method .get()
of the container returns an instance of A<? extends K>
. Therefore, the type parameter of the instance you obtain from .get()
is not fully specified. Usually, it is not very good design to return such an unspecified type. For a video presentation with Joshua Bloch, the author of Effective Java and many APIs and features in Java, showing how to improve such an API, check this: http://www.youtube.com/watch?v=V1vQf4qyMXg&feature=youtu.be&t=22m. At exactly 25'36", Joshua Bloch says "don't try to use them [wildcard types] on return values", and he explains it later. Basically, you don't get any more flexibility by using them, and just makes it painfully hard for users of the API to deal with it (you just felt the effects of doing it...).
To fix, you could simply try to change the signature of .get()
to A<K> get()
, so the container class would be:
public class Container<K> {
A<K> get() {
return new A<K>();
}
}
Since you do know that get()
is returning an instance of A<K>
, there's no reason to use the older signature: it simply makes you lose information you already know!
And if this still doesn't work, your problem might be somewhere else, and you'd need to show even more code... or better still, ask other questions! :)
Keeping in mind the PECS rule, and given the way you are using X, you should be specifying as a lower instead of upper bound:
void foo(A<? super X> a, X x)
This way no compiler errors are produced, and you have the most general signature applicable.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With