Search code examples
javaandroidannotationstype-safety

Type safe parameter in method


I'm using a library which provides custom Views. I want to create a method that accepts only custom Views from this library.

E.g. I have a PieChart and a TableChart. TableChart extends LinearLayout but PieChart extends some internal abstract chart which extends View. So, I don't have any abstract class or interface that can describe both these objects.

Currently my method looks like this:

void draw(View chart, ChartData data) {
    switch (data.getType()) {
        case PIE_CHART:
            PieChart<Numeric> pieChart = (PieChart<Numeric>) chart;
            // ...
            break;
        case TABLE_CHART:
            TableChart tableChart = (TableChart) chart;
            // ...
            break;
        // ...
    }
}

So, I can actually pass any View as a chart parameter, which doesn't look good. I was thinking of creating some annotation such as @IntDef in android but for class objects (https://developer.android.com/reference/android/support/annotation/IntDef.html). I looked at the source code and tried to create something similar but it didn't work. I'm still able to pass any View as a parameter:

  @Retention(RetentionPolicy.SOURCE)
  @Target({ElementType.ANNOTATION_TYPE})
  @interface ClassDef {
    Class<? extends View>[] value() default {};
  }

  @Retention(RetentionPolicy.SOURCE)
  @ClassDef({PieChart.class, TableChart.class})
  @interface ChartView {}

  void draw(@ChartView View chart, ChartData data) // doesn't work

I'm not really familiar with java annotations though. Does anybody know if this is possible to create such annotation that will allow to pass only specific classes as a parameter? Maybe there is some other approach to achieve type-safety? I can't change the source code of classes inside library.

Thanks.

EDIT

I provided PieChart and TableChart as examples. In my app I have about 10 types of charts.


Solution

  • So, after researching all possible solutions, I decided to use composition. I created my own wrappers around those charts and tables and then implemented my own interface:

    interface AppChart {
        void setupChart();
        void bindChart(ChartData data);
    }
    
    class AppPieChart<T> implements AppChart {
        private PieChart<T> chart;
    
        public AppPieChart(PieChart<T> chart) {
            this.chart = chart;
        }
    
        @Override
        public void setupChart() {
            //...
        }
    
        // ...
    }
    
    class AppTableChart implements AppChart {
        private TableChart chart;
    
        public AppTableChart(TableChart chart) {
            this.chart = chart;
        }
    
        @Override
        public void setupChart() {
            //...
        }
    
        // ...
    }
    

    So, my draw method is now type-safe:

    void draw(AppChart chart, ChartData data) {
        switch (data.getType()) {
            case PIE_CHART:
                AppPieChart<Numeric> pieChart = (AppPieChart<Numeric>) chart;
                // ...
                break;
            case TABLE_CHART:
                AppTableChart tableChart = (AppTableChart) chart;
                // ...
                break;
            // ...
        }
    }
    

    Thanks everyone for your help.