1

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.

Mark Jeronimus
  • 9,278
  • 3
  • 37
  • 50
  • What is 'using' party? – Coder-Man Aug 01 '18 at 15:48
  • may be using instanceof will solve the issue – Aman Chhabra Aug 01 '18 at 15:48
  • At what point do *you* know what `C` actually is? – ChiefTwoPencils Aug 01 '18 at 16:04
  • 1
    If a no arg constructor is required then why have a 1 arg constructor at all? What is the purpose of the generic? – Andrew S Aug 01 '18 at 16:13
  • What about adding a new constructor ```InteractiveChart(Chart chart)```? But I suggest you not to use generic, change all ```C``` to ```Chart```. – zhh Aug 01 '18 at 16:15
  • 1
    @zhh, he wants to instantiate `new C()`. But he won't be able to do that, because how can you know what C has a no-arg constructor? – Coder-Man Aug 01 '18 at 16:17
  • 3
    You will need to provide more information about what you are trying to do, because it's very unclear. But, from what I understand, you should create InteractiveSimpleChart, InteractiveComplexChart, InteractiveWhateverOtherChartsYouHaveChart and instantiate _them_, and not Interactive, etc. – Coder-Man Aug 01 '18 at 16:20
  • 1
    I imagine this should be doable with a bit of reflection magic, but I can't seem to get the type-munging to play nice. – Silvio Mayolo Aug 01 '18 at 16:47
  • @Wow you're totally correct, and I already had this subclass but I hoped to get rid of it. – Mark Jeronimus Aug 02 '18 at 07:23

2 Answers2

0

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.

Alvin Thompson
  • 5,388
  • 3
  • 26
  • 39
  • This doesn't actually solve the problem; they *must* have a parameterless constructor to satisfy the GUI builder reqs. – ChiefTwoPencils Aug 01 '18 at 16:46
  • @ChiefTwoPencils the parameterless constructor is still there. – Alvin Thompson Aug 01 '18 at 16:53
  • Your first suggestion -- keeping `InteractiveChart` generic, but giving it a constructor that takes any `Chart` instead of specifically `C` -- does not make any sense IMHO. But your second suggestion -- changing `InteractiveChart` to just be `InteractiveChart` (non-generic) -- may be a good one, depending on the OP's needs. – ruakh Aug 01 '18 at 18:10
  • The reason for generics is for the getter which retrieves the wrapped chart (which I forgot to put in the example in the question) – Mark Jeronimus Aug 02 '18 at 07:12
  • @MarkJeronimus I think I see what you're trying to do, and your initial feeling is right--it can't be done with generics. The least terrible way to accomplish what you need is to always return the object as a Chart and cast it as necessary (or just rely on polymorphism). – Alvin Thompson Aug 02 '18 at 17:18
  • The reason I'm pretty certain it's not possible is that generics as you know only exist at compile time. More precisely, it's a compile-time contract of what types of values you expect to see. Since the framework may need to create an instance of InteractiveChart at runtime, you simply can't know for sure at compile time what type you're going to have. And you obviously can't tell the framework to use a subclass because then the framework couldn't also use the superclass. – Alvin Thompson Aug 02 '18 at 17:34
0

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.

ruakh
  • 175,680
  • 26
  • 273
  • 307