Java generics - return subtype of declared type from method

My class implements a superclass method that returns a List<JComponent> . The returned list is read-only:

 public abstract class SuperClass { public abstract List<JComponent> getComponents(); } 

In my class, I want to return a field that is declared as List - i.e. sub-list:

 public class SubClass extends SuperClass { private List<JButton> buttons; public List<JComponent> getComponents() { return buttons; } } 

This generates a compiler error because List<JButton> not a subtype of List<JComponent> .

I can understand why it does not compile, since you should not add a JTextField to the JButtons list.

However, since the list is read-only, then "conceptually" this should be allowed. But, of course, the compiler does not know that it is read-only.

Is there a way to achieve what I want to achieve without changing the declaration of the method in the superclass and the declaration of the field in the subclass?

Thanks Calum

+6
java generics
source share
8 answers

Declare getComponents() as:

 public List<? extends JComponent> getComponents() 
+9
source share

I think I found the answer I'm looking for ...

 return Collections.<JComponent>unmodifiableList(buttons); 

I tried before:

 return Collections.unmodifiableList(buttons); 

but this was due to the List<JButton> .

Entering an explicit type parameter allows List<JButton> be processed as List<JComponent> , which is allowed by unmodifiableList() .

I am happy now ;-), and I learned something through discussion.

Calum

+6
source share

I don’t think you can do what you want without changing the class signature of the superclass or declaring a list of subclasses. The superclass rigidly defines the return type as a JComponent. It is not possible to make your return type except JComponent.

If it is read-only, I would do something like:

 public class SubClass extends SuperClass { private List<JComponent> buttons = new ArrayList<JComponent>(); public void addButton(JButton button) { buttons.add(button); } public List<JComponent> getComponents() { return Collections.unmodifiableList(buttons); } } 

If you can change the superclass, you can try something like:

 public abstract class SuperClass<E extends JComponent> { public abstract List<E> getComponents(); } public class SubClass extends SuperClass<JButton> { private List<JButton> buttons = new ArrayList<JButton>(); public List<JButton> getComponents() { return Collections.unmodifiableList(buttons); } } 
+4
source share

Returning a generic type with a wildcard is not a good idea, as it makes the client think about what is coming back ... It will also make the client code more cumbersome and difficult to read.

+1
source share

For this you need to expand the generics. :)

 public abstract class SuperClass { public abstract List<? extends JComponent> getComponents(); } public class SubClass extends SuperClass { private List<JButton> buttons; public List<? extends JComponent> getComponents() { return buttons; } } 
0
source share

Hmm not sure

The usual way to set this up is that the super looks like

 public abstract class SuperClass { public abstract List<? extends JComponent> getComponents(); } 

Not sure how to do this without changing this.

0
source share

The whole family of pedigrees is to provide a type of compilation-time type security for things like the "type" of a collection. Unfortunately for you, List<JButton> not a subtype of List<JComponent> . The reason for this is simple and is as follows:

 List<JComponent> jc; List<JButton> jb = new ArrayList<JButton>(); //if List<JButton> was a sublcass of List<JComponent> then this is a valid assignment jc = jb; jc.add(new JCheckBox()); //valid, as jc accepts any JComponent JButton b = jb.get(0); //this will throw a ClassCastException, rendering Generic type-safety pointless 

Generics contradict each other in this way, your initial desire could also override the method that returned String with the one returned by Float

I think this is a good rule to be very careful when developing a universal class. Do you really need generics? How will it be used? Some sample projects end in terrible structures, and average users should revert to the original version. The JTable filtering JTable is a great example: when you want to implement a filter chain (which you will certainly be!), It all falls apart. The fact; in this case, generics were added at a cost and with little or no benefit.

0
source share

You can stream using @SuppressWarnings . I believe this would be appropriate in this case, just make sure you document why in the comment.

Alternatively, do the following:

 public List<JComponent> getComponents() { return new ArrayList<JComponent>( buttons ); } 

Yes, I know this makes a copy, and the list is already read-only. But until the profiler tells you otherwise, I would suggest that the fine is small.

@Calum: I agree that use? -expressions in inverse types is a bad form because calling the code cannot do this, for example:

 List<JComponent> list = obj.getComponents(); 
-one
source share

All Articles