在方法中键入安全参数

Type safe parameter in method

我正在使用一个提供自定义 View 的库。我想创建一个只接受来自该库的自定义 View 的方法。

例如我有一个 PieChart 和一个 TableChartTableChart 扩展了 LinearLayout,但 PieChart 扩展了一些内部 abstract 图表,后者扩展了 View。所以,我没有任何可以描述这两个对象的抽象 class 或接口。

目前我的方法是这样的:

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;
        // ...
    }
}

所以,我实际上可以将任何 View 作为 chart 参数传递,这看起来不太好。我正在考虑在 android 中创建一些注释,例如 @IntDef 但对于 class 对象 (https://developer.android.com/reference/android/support/annotation/IntDef.html)。我查看了源代码并尝试创建类似的东西,但没有成功。我仍然可以将任何 View 作为参数传递:

  @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

虽然我对 java 注释不是很熟悉。有谁知道是否可以创建这样的注释,只允许将特定的 classes 作为参数传递? 也许还有其他方法可以实现类型安全?我无法更改库中 classes 的源代码。

谢谢。

编辑

我提供了 PieChartTableChart 作为示例。在我的应用程序中,我有大约 10 种类型的图表。

您可以定义两种 public 方法,一种采用 PieChart,另一种采用 TableChart。这将为您提供类型安全。如果存在共享逻辑,那么您可以从 public 方法中调用私有或受保护的方法。注释方法将不起作用。

您可以这样做的一种方法是:

void draw(View chart, ChartData data) throws Exception {
    if(!(chart instanceof TableChart || chart instanceof PieChart)){
        throw new IllegalArgumentException();
    }
    switch (data.getType()) {
        case PIE_CHART:
            PieChart<Numeric> pieChart = (PieChart<Numeric>) chart;
            // ...
            break;
        case TABLE_CHART:
            TableChart tableChart = (TableChart) chart;
            // ...
            break;
        // ...
    }
}

所以,在研究了所有可能的解决方案之后,我决定使用组合。我围绕这些图表和表格创建了自己的包装器,然后实现了我自己的 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() {
        //...
    }

    // ...
}

所以,我的 draw 方法现在是类型安全的:

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;
        // ...
    }
}

感谢大家的帮助。

写注释是一个不错的选择,因为它不需要你改变你的源代码,它不会强加任何运行-时间开销,并且它可以给你一个编译时保证,客户端正确使用库。

仅仅写注释是不够的。编译时,您还需要提供一个注解处理器,如果您误用注解,它会发出编译错误。因此,您显示的代码是一个好的开始,但它缺少注释处理器。

您可以编写自己的注释处理器,也可以使用现有的注释处理器。您可能会发现有用的一个是 Checker Framework. It seems well-suited to your use case: you could use the Subtyping Checker 并使用诸如

之类的命令编译您的代码
javac -processor org.checkerframework.common.subtyping.SubtypingChecker \
-Aquals=ChartView MyFile.java