在方法中键入安全参数
Type safe parameter in method
我正在使用一个提供自定义 View
的库。我想创建一个只接受来自该库的自定义 View
的方法。
例如我有一个 PieChart
和一个 TableChart
。 TableChart
扩展了 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 的源代码。
谢谢。
编辑
我提供了 PieChart
和 TableChart
作为示例。在我的应用程序中,我有大约 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
我正在使用一个提供自定义 View
的库。我想创建一个只接受来自该库的自定义 View
的方法。
例如我有一个 PieChart
和一个 TableChart
。 TableChart
扩展了 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 的源代码。
谢谢。
编辑
我提供了 PieChart
和 TableChart
作为示例。在我的应用程序中,我有大约 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