代码之家  ›  专栏  ›  技术社区  ›  Gennadii Saprykin

在方法中键入安全参数

  •  1
  • Gennadii Saprykin  · 技术社区  · 9 年前

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

    E、 我有一个 PieChart 和一个 TableChart . 图表 延伸 LinearLayout 但是 饼图 扩展一些内部 abstract 扩展的图表 看法 因此,我没有任何抽象类或接口可以描述这两个对象。

    目前我的方法如下:

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

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

      @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注释并不太熟悉。有人知道这是否可能创建这样的注释,只允许将特定的类作为参数传递吗? 也许还有其他方法可以实现类型安全?我无法更改库中类的源代码。

    谢谢

    编辑

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

    4 回复  |  直到 9 年前
        1
  •  1
  •   Paul Howells    9 年前

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

        2
  •  1
  •   mernst    9 年前

    编写注释是一个很好的选择,因为它不需要您更改源代码,也不会强加任何运行时开销,而且它可以为您提供编译时保证客户端正确使用库。

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

    您可以编写自己的注释处理器,也可以使用现有的注释处理器。你可能会发现有用的是 Checker Framework 。它似乎非常适合您的用例:您可以使用 Subtyping Checker 并使用以下命令编译代码

    javac -processor org.checkerframework.common.subtyping.SubtypingChecker \
    -Aquals=ChartView MyFile.java
    
        3
  •  0
  •   Gsquare    9 年前

    一种方法是:

    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;
            // ...
        }
    }
    
        4
  •  0
  •   Gennadii Saprykin    9 年前

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

    谢谢大家的帮助。