简体   繁体   中英

new List<A & ? extends B>();

If you know how to run the title line, just scroll down to tl;dr or write an answer!

I wrote the rest of the text to avoid the XY problem . It doesn't matter anymore, when this is done.


Context

I want to create an object that takes a generic (List, Map, whatever).

The objects within the list should fullfill 2 criterias:

  1. having mymethod() (which I realized from creating an Interface having this as a method head)
  2. Extending JComponent

How I tried to make this work:

  1. I created multiple classes, any of these are implementing MyInterface and are extending a JComponent (subclasses of it). That's where I run in my title problem: new List<MyInterface extends JComponent);
  2. Another idea: creating an abstract class (that fits all criteria) instead of an Interface. Problem: The concrete classes I need do not extend the same class.
  3. I thought of Generics, just like: public abstract class MyClass<X extends JComponent> extends X , but this can't work – for obvious reasons. (eg I have to implement the abstract methods from a superclass, but if it's a Generic I cannot know by now.)

tl;dr

I run in a problem which could easily be made if you can make this line working:

new List<A extends B>();

or, introduced by @khelwood: new List();

where:

  • A is an Interface (just as I could declare a new List<MyInterface>()
  • and any item extends ? extends B ? extends B (example: it can be a JTextField or a JTextArea or whatever they like, but it has to be a (subclass of) JComponent (JComponent = B))

If you want to be able to create a list of things which extend JComponent and implement your interface, and add things to such a list, you need to define a concrete type which implements both:

List<ThatType> list = new ArrayList<>();
list.add(new ThatType());

You can declare a type variable for this, in order to write a generic method:

<T extends JComponent & YourInterface> void something(T thing) {
  // ...
}

<Something extends SomethingElse> only works in the declarations of type variables.

Moreover, you don't create a list of Somethings that extends SomethingElse : you create a list of Somethings , and whether or not Something extends SomethingElse depends upon how those types are defined.

To make this more concrete, you don't make a new List<String extends Object> : you make a List<String> , and String happens to extend Object .

You need to declare the type variables appropriately, eg:

class Foo<B> {
  <A extends B> List<A> list() {
    return new List<>();
  }
}

You say that:

Another idea: creating an abstract class (that fits all criteria) instead of an Interface. Problem: The concrete classes I need do not extend the same class.

but at the same time you say that they extend JComponent . The only possibility for this to occur is that they all transitively extend JComponent . So at some point up the inheritance hierarchy there is a direct inheritance from JComponent .

If this is still your code, this is the point where you could introduce another class, instead of a JComponent that extends JComponent and implements your interface.

If not then I think it could be impossible to achieve using only syntax.

I don't think the type system allows you to do exactly what you want and get compile-time type safety.

One approximation is the following:

List<JComponent> componentList = new ArrayList<>();
List<MyInterface> myInterfaceList = (List) componentList;

If you don't mind the compile-time warning, you now have two views on the same list: one shows the content as JComponent 's and the other as MyInterface 's.

You can add the objects to either view and then read them from the view that provides the right type for the context.

Naturally, when you add an object the compiler will not check that the inserted object subclasses both JComponent and MyInterface . That is your responsibility.

If you want more (run-time) type safety, you can go this way:

List rawList = new ArrayList();
List checkedList = Collections.checkedList(rawList, JComponent.class);
List doublyCheckedList = Collections.checkedList(checkedList, MyInterface.class);
List<JComponent> componentList = doublyCheckedList;
List<MyInterface> myInterfaceList = doublyCheckedList;

If you only insert into doublyCheckedList , at runtime the list will check that the inserted objects subclass both JComponent and MyInterface . When reading, you can use the two views as in my previous fragment.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM