Carlo Amaglio
Carlo Amaglio

Reputation: 21

Generics that extends more than one type

Assign to a variable an object that extends a class and implements an Interface at the same time. I have a method like this

public static <T extends Component & MyInterface> T instance() {
  if (test1) return new MyLabel();
  if (test2) return new MyCombo();
  if (test3) return new MyText();
}

class MyLabel extends JLabel implements MyInterface {
  ...
}

class MyCombo extends JComboBox implements MyInterface {
  ...
}

class MyText extends JTextField implements MyInterface {
  ...
}

this means the instance() returned object is a Component AND implements MyInterface. and I can do something like

instance.setEnable(true); // calling a Component method
instance.foo();           // calling a MyInterface method

Now I want to assign the returned value to a variable: how to declare the variable in order to bring with the variable all the Generics info?

I expect to be able to do something like this:

static <T extends Component & MyInterface> T myVar = instance();
myVar.setEnable(true); // calling a Component method
myVar.foo();           // calling a MyInterface method

and also this:

static void f1(Component c) {}
static void f2(MyInterface c) {}
f1(myVar);
f2(myVar);

In my opinion the question is different from the one in Why can't I use a type argument in a type parameter with multiple bounds? because I'm not using a type parameter inside a generic class declaration.

Upvotes: 1

Views: 130

Answers (2)

Carlo Amaglio
Carlo Amaglio

Reputation: 21

Based on John Bollinger suggestion I do some other experiments and found a possible "solution" (however not as simple as I required). But I think my wrap() method is different from what John meant.

public final class Wrap<X extends Component & MyInterface> {
    public final X x;
    public Wrap(X x) {
        this.x = x;
    }
}
public static <X extends Component & MyInterface> Wrap<X> wrap(X x) {
    return new Wrap<X>(x);
}

static Wrap<?> myVar = wrap(instance());
myVar.x.setEnabled(true);    // Component method
f1(myVar.x);                 // Component parameter
myVar.x.foo();               // MyInterface method
f2(myVar.x);                 // MyInterface parameter

Upvotes: 1

John Bollinger
John Bollinger

Reputation: 180201

I expect to be able to do something like this:

static <T extends Component & MyInterface> T myVar = instance();
myVar.setEnable(true); // calling a Component method
myVar.foo();           // calling a MyInterface method

I'm afraid not. Java does not offer generic variables, and for the most part it wouldn't make sense to do so. You may declare the type of an instance variable as a type parameter of its class, but you cannot give any variable its own, independent type parameter like you can a method. There is no way to declare a variable that has two types.

If you want to be able to make use of both facets of myVar, as shown in the example, then its type must be declared as a specific type that extends or implements both supertypes. There are two main alternatives for that:

  1. Make the instance() method return such a type. This is probably the most natural solution, since that method doesn't depend on T for any purpose other than to hang supertypes upon.

    public class MyInterfaceComponent extends Component implements MyInterface {
        // ...
    }
    
    MyInterfaceComponent myVar = instance();
    
  2. Dynamically generate a wrapper object of such a type that adapts general objects that extend the class and implement the interface to a particular type that does so.

    MyInterfaceComponent myVar = wrap(instance());
    

The latter makes sense to me only if there's a possibility that you need to handle externally-provided objects that extend the class and implement the interface, but whose specific class you cannot control.

Upvotes: 0

Related Questions