Empty constructor in parametrized class

152 Views Asked by At

I have a class InteractiveChart that wraps a Chart (interface) and adds interactivity to it by mapping user interactions to setter calls on Chart.

1) Because different subclasses of Chart can have different 'extra' capabilities I want to have a getter in the wrapping class that returns the wrapped Chart (without needing to do an unchecked cast!). The only way I know to do this is by having a generic parameter on the wrapping class.

2) This wrapping class is also meant to be used in NetBeans' GUI Builder, which has the constraint that it requires an empty constructor. However the runtime type of the generic parameter is determined by the code instantiating the it, and I have no control over how NetBeans instantiates it. In this case I want it to wrap a SimpleChart and if the user wants to wrap something else, they'd have to add a JPanel placeholder to the GUI and add the chart to that in user code. Because of the same constraint I can't have a generic parameter on the wrapping class.

public class InteractiveChart<C extends Chart> extends JPanel {
    private final C wrappedChart;
    public InteractiveChart() {
        // Compiler error: at this point C can be any other subclass of Chart
        this(new SimpleChart()); 
    }

    public InteractiveChart(C chart) { wrappedChart = chart; }

    public C getWrappedChart() { return wrappedChart; }
}

How can I solve the dilemma between 1 and 2?

My current best solution is to create a subclass of InteractiveChart called InteractiveSimpleChart that fixates the generic parameter. I hope to find a way to eliminate this subclass, as every time I add a new kind of Chart I'd have to add a new wrapper for interactivity too.

2

There are 2 best solutions below

6
Alvin Thompson On

I'm probably not explaining this well, but the type C can only be a single type at once. When you say C extends Chart, you're restricting what type that C can be, but at the end of the day it can only be one of those types. In other words, any given InteractiveChart might be an InteractiveChart of type Chart or an InteractiveChart of type SimpleChart, but not both at the same time.

So as it's written, your second constructor must take that exact type. C extends Chart does not mean "any class that extends Chart". It means "a specific class, which happens to extend Chart". You get the compile error because the constructor requires a class of type C, but you're trying to supply it with a class of type SimpleChart. C may not be a SimpleChart. It may be a Chart or maybe SomeOtherChart for example.

TL;DR: in order to require 'any class that extends Chart' in your second constructor, you simply use Chart instead of C. :)

public InteractiveChart(Chart chart) { .. }

Problem solved.

Of course, this brings up a better question, which is do you really need to use generics here? So far you haven't shown anything that requires it.

0
ruakh On

So, from what I understand, your requirements are:

  • You have some sort of GUI-builder framework that, for some unfortunate reason, just gets a class-name or Class<?> instance or whatnot and uses the no-arg constructor. For this framework, you want InteractiveChart<SimpleChart> with the chart being new SimpleChart().
  • You also have other code that benefits from greater flexibility: you want to create InteractiveChart<C> instances for different chart-types besides just SimpleChart.

Do I have that right?

The problem, then, is that Java doesn't let you define a constructor that's only for InteractiveChart<SimpleChart> — any constructor for InteractiveChart has to apply to arbitrary C — so you're trying to define a constructor for arbitrary InteractiveChart<C> that simply assumes that C is SimpleChart, which (I hope you'll agree) is not a great assumption to be making here, and is not an assumption that Java is on board with.

One simple workaround is to create a subclass of InteractiveChart<SimpleChart> for your GUI-builder framework to instantiate:

public class SimpleInteractiveChart extends InteractiveChart<SimpleChart> {
    public SimpleInteractiveChart() {
        super(new SimpleChart());
    }
}

Then your GUI-builder framework can use new SimpleInteractiveChart(), and your other code can still use InteractiveChart<C> with whatever type argument it wants.