Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Sharing a wildcard in Java generics

Suppose I have an interface

interface Foo<T> { 
    void foo(T x); 
    T bar() 
}

and an object of this type with unknown parameter: Foo<?> baz. Then I can call baz.foo(baz.bar()).

However, now I need to put the value baz.bar() into a collection and call baz.foo() on it later on. Something like

List<???> list; // can I tell the compiler this is the same type as baz's wildcard?
list.add(baz.bar());
...
baz.foo(list.get(1));

This doesn't work either:

List<Object> list;
list.add(baz.bar());
...
baz.foo((???) list.get(1)); // I can't write down the type I need to cast to

Is there a way to do this?

EDIT: The above was oversimplified from my actual situation. Say we have

class Bar {
    private final Foo<?> foo;
    private List<???> list; // the type argument can be selected freely

    Bar(Baz baz) {
        foo = baz.getFoo(); // returns Foo<?>, can't be changed 
    }

    void putBar() {
        list.add(foo.bar());
    }

    void callFoo() {
        foo.foo(list.get(0));
    }
}
like image 474
Alexey Romanov Avatar asked Sep 16 '26 04:09

Alexey Romanov


1 Answers

You can wrap your logic in a generic method like this:

public <T> void myMethod(Foo<T> baz) {
  List<T> list; // initialise it...
  list.add(baz.bar());
  // ...
  baz.foo(list.get(1));
}

That's the only way I know of, to achieve what you want, without resorting to unsafe casting.

After the OP's edit:

As others have mentioned, the outer class Bar needs to know the common generic type T. Besides, with your constraint of not being able to change the signature of Baz.getFoo(), I guess you'll have to unsafe cast Foo<?> to Foo<T> in the constructor of Bar<T>:

class Bar<T> {

    private final Foo<T> foo;
    private List<T> list;

    Bar(Baz baz) {
        // Since baz cannot be changed, you will have to
        // unsafe cast Foo<?> to Foo<T> here.
        foo = (Foo<T>) baz.getFoo();
    }

    void putBar() {
        list.add(foo.bar());
    }

    void callFoo() {
        foo.foo(list.get(0));
    }
}

Alternatively (as Foo, Bar, Baz are not good means of understanding what your real-life use-case does), you can still avoid generifying Bar like this:

class Bar {

    private final Foo<Object> foo;
    private List<Object> list;

    Bar(Baz baz) {
        // Since baz cannot be changed, you will have to
        // unsafe cast Foo<?> to Foo<Object> here.
        foo = (Foo<Object>) baz.getFoo();
    }

    void putBar() {
        list.add(foo.bar());
    }

    void callFoo() {
        foo.foo(list.get(0));
    }
}
like image 97
Lukas Eder Avatar answered Sep 17 '26 20:09

Lukas Eder



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!