Generics: How to capture a wildcard? - java

No, this question is not about the difference between ? and T; it is about how I turn a < ? > argument into a named < T >.
Consider this example code:
import java.io.Serializable;
class A<T extends Serializable> {
<S extends Serializable> void bar(S arg) { }
void bar2(T arg) { }
}
public class B {
A<? extends Serializable> myA = null;
<T extends Serializable> void foo(T arg) {
myA.bar(arg);
myA.bar2(arg);
}
}
My problem is the fact that the above doesn't compile; the call to bar2() gives me
The method bar2(capture#2-of ? extends Serializable) in the type
A<capture#2-of ? extends Serializable> is not applicable for the arguments (T)
I guess the "reason" for that is that myA is a < ? extends Serializable >; but the T in B.foo ... is well, a named T; and not the wildcard ?.
One way to "fix" this would be to make < T extends Serializable > a type parameter for class B ... but that basically conflicts with my other usage of that class. Meaning: I ended up writing down B and its member myA like this - exactly because I don't want to parameterize the B class. So, I started with only the bar2() method; and then added bar() later on ... but that doesn't really help either.
So - is there a way to for my class B to use A.bar2() as "intended" in my example code?
EDIT: and just to be precise - the bar() method doesn't help me; as the "S" there ... isn't compatible with the "T" that I am really using in my A class.

You're in a dead end.
A<? extends Serializable> myA = ...;
So myA is a A of something unknown. It could be a A<String>, or a A<Integer> or a A<Banana>. You just don't know. Let's say it's a A<Banana>.
<T extends Serializable> void foo(T arg) {
So foo() accepts any serializable: String, Integer, Banana, Apple, anything. Let's say it's called with an Integer as argument.
myA.bar2(arg);
So, based on the assumptions above, that would call bar2() with an Integer as argument, although myA is a A<Banana>. That can't be accepted by the compiler: it's clearly not type-safe. B must be made generic.

#JBNizet already explains why this can't be done. This answer aims to explain the possible options you have (one of them being rightly pointed out by in your question but that's not the only option).
Make B take a type parameter
class B<T extends Serializable> {
A<T> myA = null;
void foo(T arg) {
myA.bar(arg);
myA.bar2(arg);
}
}
Make B extend from A (If B passes the is-a test for A)
class B extends A<String> {
public void foo(String arg) {
bar2(arg);
bar(1);
}
}
The difference between the two options is that in 1) both bar and bar2 need to be passed the same type where as in 2) bar and bar2 can be passed different types since bar2 is bound by the type parameter declared for A where as bar is bound by the type parameter declared for the method.

Related

Is it possible in Java to narrow down generic type in subclass of generic class?

is it possible in Java to narrow down the generic type in a subclass of a generic class?
My case looks like this:
class C<T> {
protected T foo() {...}
protected void bar(Supplier<T> foobar) {...}
}
class A<T extends B> extends C<T> {
public void someMethod(){
B b = foo(); // Compiler does not complain
bar(() -> createB()); // Compiler complains with "Type mismatch: cannot convert from B to T"
}
}
Maybe what I did is totally stupid, but I don't get why the compiler does not complain about foo(), but about bar().
Any advice on how to do this, or why not to do this at all, is highly appreciated :)
Kind regards,
Thomas
It's a variance issue. You have A<T extends B> a subtype of C<T>. We also have
class C<T> {
protected T foo() {...}
protected void bar(Supplier<T> foobar) {...}
}
Now, when you do
B b = foo();
foo() returns T. And we know (from the class declaration) that T extends B is true. It's always safe to upcast to a supertype, so every T is a B. Now consider
bar(() -> createB());
createB is, I presume, a function which returns a B. bar is expecting a Supplier<T>. That is, bar is expecting something which produces a T. Does createB produce a T? Well, it produces a B. Can we upcast a B to a T? No, we know T extends B, but to upcast B to T we would need to know the opposite: that B extends T.
The T in bar appears in argument position (contravariant), while the T in foo appears in return position (covariant). This accounts for the difference in the ability to cast. Had we written
class A<T super B> extends C<T> {
...
}
Then you'd be able to do the bar example but not the foo example, because we have the opposite relation.
See What is PECS, which is related to this question. It's more about call-site variance, but the discussion of variance is still relevant nonetheless.
According to #luk2302 comment: createB hast to be generic itself so that you can parameterize IT to return a T extends b

Generics and inheritance issue in Java

Please, consider my code:
class A {
}
class B extends A {
}
class AWrapper<T extends A> {
}
class BWrapper<T extends B> extends AWrapper<T> {
}
So, I have C extends B etc, and CWrapper<T extends C> extends BWrapper<T> etc.
Now, I use these classes this way:
class Test {
private Class<? extends AWrapper<? extends A>> klass;//LINE X
public Test() {
this.klass = BWrapper.class;//LINE Z
}
}
At LINE X I need to set different classes - AWrapper, BWrapper, CWrapper etc. However, at LINE Z I get an error. Could anyone help to fix it?
imho, what you are looking for is :
private Class<? extends AWrapper> klass;
even if AWrapper is raw. At least this way you could say that your parameter takes "a class that is subtype (or self) of AWrapper". But that should be the thing you care about anyway. Generics are not preserved at runtime, so no matter what Class you have there, it is going to be without its inferred parameters.

Java Generic Conversion not as expected

If someone could resolve this puzzle for me. I am sure I am missing something !
interface a { }
class b implements a { }
class c extends ArrayList<b> { }
class d {
d(ArrayList<a> param) {}
}
class e {
public static void main(String[] arg) {
d newd = new d(new c());
}
}
This code has an error:
Error d(ArrayList<a>) is not applicable (actual argument c cannot be converted to ArrayList<a> by method invocation conversion)
Surely class c can be converted to ArrayList<a> as c extends ArrayList<b> and b implements a !
I have tried explicit conversion but it does not help.
It does not make sense either to change class c extends ArrayList<b> to extends ArrayList<a> as the purpose of c to to be a collection of b and the interface a is merely for display purposes. Also class d is a generic display class that relies on functionality expressed in the interface a and so makes no sense to change that either.
Advice would be handy !
This should suit your needs:
class d {
d(ArrayList<? extends a> param) {
}
}
c isn't an ArrayList<a> but an ArrayList<? extends a> since b is a subtype of a.
The problem is that an ArrayList is not the same thing as an ArrayList, even if b extends A. What would happen if D called param.add(new AImpl()), where AImpl was some other implementation of interface A?
For more details, see The Java Generics Tutorial section on inheritance.
(As an aside, it's also generally not a good idea to extend an ArrayList - most of the time, you want to be wrapping it, not extending it.)
Please respect java naming conventions:
interface A {
}
class B implements A {
}
class C extends ArrayList<B> {
}
class D {
D(ArrayList<A> param) {
}
}
class E {
public static void main(String[] arg) {
D newd = new D(new C());
}
}
So your assumption is that ArrayList<B> is castable to an ArrayList<A> as B implements A. This is not true, this is not how generics work. You simply cannot cast generic types.
Try doing something like:
final ArrayList<Object> myNewArrayList = new ArrayList<String>();
To distill the problem.
You need to use bounded type parameters to solve this. Changing your D class to:
class D {
D(ArrayList<? extends A> param) {
}
}
Would make it work as now you're saying: I want a collection of any type as long as it's an A.
It should be:
d(ArrayList<? extends a> param) {}
ArrayList is not a subclass of ArrayList because the former can have a's added to it while the latter cannot.

Java Generics: Multiple Inheritance in Bounded Type Parameters <T extends A & I>

I am about to create a factory which creates objects of a certain type T which extends a certain class A and another interface I. However, T must not be known. Here are the minimum declarations:
public class A { }
public interface I { }
This is the factory method:
public class F {
public static <T extends A & I> T newThing() { /*...*/ }
}
This compiles all fine.
When I try to use the method the following works fine:
A $a = F.newThing();
...while this does not:
I $i = F.newThing();
The compiler complains:
Bound mismatch: The generic method newThing() of type F is not applicable for the arguments (). The inferred type I&A is not a valid substitute for the bounded parameter
I can't understand why. It is clearly stated that "newThing returns something of a certain type T which does extend the class A and implement the interface I". When assigning to A everything works (since T extends A) but assigning to I does not (because of what?, clearly the thing returned is both an A and an I)
Also: When returning an object, say B of the type class B extends A implements I, I need to cast it to the return type T, although B matches the bounds:
<T extends A & I> T newThing() {
return (T) new B();
}
However, the compiler does not throw any warnings like UncheckedCast or the like.
Thus my question:
What is going wrong here?
Is there an easy away to achieve the desired behavior (i.e. assigning to a variable of static type A or I), like there is in solving the return-type-problem by casting, in the factory method?
Why does the assignment to A work, while to I does not?
--
EDIT: Here the complete code snippet which totally works using Eclipse 3.7, project set up for JDK 6:
public class F {
public static class A { }
public static interface I { }
private static class B extends A implements I { }
public static <T extends A & I> T newThing() {
return (T) new B();
}
public static void main(String... _) {
A $a = F.newThing();
// I $i = F.newThing();
}
}
EDIT: Here is a complete example with methods and invocation which does work at runtime:
public class F {
public static class A {
int methodA() {
return 7;
}
}
public static interface I {
int methodI();
}
private static class B extends A implements I {
public int methodI() {
return 12;
}
}
public static <T extends A & I> T newThing() {
return (T) new B();
}
public static void main(String... _) {
A $a = F.newThing();
// I $i = F.newThing();
System.out.println($a.methodA());
}
}
As for the second question:
Consider this case:
class B extends A implements I {}
class C extends A implements I {}
Now, the following uses type inference:
<T extends A & I> T newThing() {
return (T) new B();
}
So you could call this:
C c = F.newThing(); //T would be C here
You see that T could be anything that extends A and I you can't just return an instance of B. In the case above the cast could be written as (C)new B(). This would clearly result in an exception and thus the compiler issues a warning: Unchecked cast from B to T - unless you're supressing those warnings.
This doesn't do what you expect it to. T extends A & I indicates that the caller can specify any type that extends A and I, and you'll return it.
I think that one way to explain it is by replacing the type parameter with the actual type.
The parameterized signature of the methods is:
public static <T extends A & B> T newThing(){
return ...;
}
The <T extends A & B> is what is called a type parameter. The compiler would expect that this value is actually substituted with the actual type (called type argument) when you actually use it.
In the case of your method the actual type is decided by means of type inference. That is, <T extends A & B> should be replaced by a real existing type that extends A and implements B.
So, let's say that classes C and D both extends A and implements B, then if your signature were like this:
public static <T extends A & B> T newThing(T obj){
return obj;
}
Then, by type inference, your method would be evaluated as follows:
public static C newThing(C obj){
return obj;
}
if you invoke with newThing(new C()).
And would be as follows
public static D newThing(D obj){
return obj;
}
if you invoke with newThing(new D()).
This would compile just fine!
However, since you are not actually providing any kind of type to validate type inference in your method declaration, then the compiler could never be sure what is the actual type (type argument) of your type parameter <T extends A & B>.
You might expect that the actual type is C, but there may be thousands of different classes that satisfy that criteria. Which of those should the compiler use as the actual type of your type argument?
Let's say that C and D are two classes that extend A and implements B. Which of these two actual types should the compiler use as type argument for your method?
You could have even declared a type argument for which there is not even an existing type that you can use, like saying something that extends Serializable and Closable and Comparable and Appendable.
And perhaps there is not a class in the whole world that satisfies that.
As such, you must understand that the type parameter here is just a requirement for the compiler to validate the actual type that you use, a placeholder for the actual type; and this actual type must exist at the end and the compiler will use it to replace appearances of T. Therefore the actual type (type argument) must be inferable from the context.
Since the compiler cannot tell with certainty which is the actual type that you mean, basically because there is no way to determine that by type inference in this case, then you are forced to cast your type, to ensure the compiler that you know what you are doing.
As such, you could implement your method using type inference like this:
public static <T extends A & B> T newThing(Class<T> t) throws Exception{
return t.newInstance();
}
This way, you would be actually telling the compiler what is the actual type argument to be used.
Take into account that when the bytecodes are generated, the compiler must substitute T for a real type. There is no way to write method in Java like this
public static A & B newThing(){ return ... }
Right?
I hope I have explained myself! This is not simple to explain.
Simplest solution is create an abstract base class that extends and implements whatever class and interfaces you want and return that type. It doesn't matter that you're constraining your return type to extend this base class as you were already constraining the return type to its superclass.
eg.
class C {}
interface I {}
abstract class BaseClass extends C implements I {}
// ^-- this line should never change. All it is telling us that we have created a
// class that combines the methods of C and I, and that concrete sub classes will
// implement the abstract methods of C and I
class X extends BaseClass {}
class Y extends BaseClass {}
public class F {
public static BaseClass newThing() {
return new X();
}
public static void main(String[] args) {
C c = F.newThing();
I i = F.newThing();
}
}

Java: using generic wildcards with subclassing

Say I have a class Foo, a class A and some subclass B of A. Foo accepts A and its sublclasses as the generic type. A and B both require a Foo instance in their constructor. I want A's Foo to be of type A , and B's Foo to be of type B or a superclass of B. So in effect, So I only want this:
Foo<X> bar = new Foo<X>;
new B(bar);
to be possible if X is either A, B, or a both subclass of A and superclass of B.
So far this is what I have:
class Foo<? extends A>{
//construct
}
class A(Foo<A> bar){
//construct
}
class B(Foo<? super B> bar){
super(bar);
//construct
}
The call to super(...) doesn't work, because <A> is stricter than <? super B>. Is it somehow possible to use the constructor (or avoid code duplication by another means) while enforcing these types?
Edit: Foo keeps a collection of elements of the generic parameter type, and these elements and Foo have a bidirectional link. It should therefore not be possible to link an A to a Foo.
If you change the A constructor to:
class A(Foo<? extends A> bar){
//construct
}
will it do what you want ?
If you really want to limit the constructor of A to Foo then you need to provide another protected method (aka usable from derived classes) to set the Foo instance.
Something like this:
public class A {
Foo<?> foo;
public A(Foo<A> foo) {
setFoo(foo);
}
protected A() {
}
protected void setFoo(Foo<?> foo) {
this.foo = foo;
}
}
and B
public class B extends A {
public B(Foo<? super B> foo) {
setFoo(foo);
}
}
now this works:
new A(new Foo<A>());
new A(new Foo<B>()); // this fails compilation
new B(new Foo<B>());
In order for foo element in A to be properly typed you might need to make A a parametrized class too.
The only way to do this would be to have...
class A(Foo<? extends A> bar) {
//construct
}
But it appears this isn't what you want. You can't have the other approach because when you create an instance of B you are also creating an instance of A (that is part of the B instance). So B can't take in special fields for the parts in A. I'm not sure why you wouldn't allow A's foo to be of type B, could you perhaps expand on that?
The following setup compiled for me:
public interface MarkerInterface {
}
public class A implements MarkerInterface {
public A(Foo<A> fooA) {
}
}
public class SuperB implements MarkerInterface {
}
public class B extends SuperB {
public B(Foo<? super B> fooB) {
}
}
And with the main method:
public static void main(String[] args) {
B b = new B(new Foo<SuperB>());
}
Is this what you're looking for?
Java generics are powerful and well designed; nevertheless we sometimes find them lacking. I don't think there's a good way to do what you want.
The easiest thing to do is to change the declaration of Foo<X> to Foo<X extends A>. If that's not acceptable, you can subclass Foo like this:
class Foo<X> { }
class FooA<X extends A> extends Foo<X> { }
class A {
public A(FooA<? extends A> foo) { }
}
class B extends A {
public B(FooA<? super B> foo) {
super(foo);
}
}
(Note: if you have trouble following, when you see Foo, think ArrayList.)
This has the obvious disadvantage that you have to use FooA rather than Foo, hindering code reuse.

Categories

Resources