GhostCat
GhostCat

Reputation: 140447

Generics: How to capture a wildcard?

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.

Upvotes: 1

Views: 439

Answers (2)

Chetan Kinger
Chetan Kinger

Reputation: 15212

@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.

Upvotes: 1

JB Nizet
JB Nizet

Reputation: 691725

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.

Upvotes: 2

Related Questions