我有一个类Column
,它描述了类似SQL的表的列:
public interface Column<S extends Schema<S>, T> {
default String encode(final T value) {
return value.toString();
}
}
一个列包含某种类型(整数,字符串,...),并且有一个实用程序函数可以将该类型的实例转换为字符串(这对于日志记录非常有用)。
接下来,我有一个类Schema
,它描述了表的架构:
public interface Schema<S extends Schema<S>> {
List<Column<S, ?>> getColumns();
}
架构包含列列表。
让我们创建一个只有一列的具体架构:
public static class MySchema implements Schema<MySchema> {
public static final Column<MySchema, Integer> ID = new Column<MySchema, Integer>(){};
@Override
public List<Column<MySchema, ?>> getColumns() {
return Collections.singletonList(ID);
}
}
接下来,我有一个类MyData
,其中包含与架构相对应的数据:
public static class MyData<S extends Schema<S>> {
public <T> T get(final Column<S, T> column) {
return (T) new Integer(164); // actual implementation left out
}
}
手动编码列的值非常简单:
final MySchema s = new MySchema();
final MyData<MySchema> d = new MyData<>();
System.out.println("encoded identifier: " + MySchema.ID.encode(d.get(MySchema.ID)));
现在,让我们动态尝试:
for (final Column<MySchema, ?> column : s.getColumns()) {
System.out.println("encoded identifier: " + column.encode(d.get(column)));
}
这不起作用,因为d.get(column)
被推断为capture<?>
,这不是Column.encode()
所接受的。
我该如何解决这个问题?我理解这里出了什么问题(Column.encode(T value)
只接受我们现在没有的T
),但我找不到一个不会丢失类型的解决方案-garanty我们只接受T
强制执行。
作为一个小提琴,这里是完整的代码:
public class Test {
public interface Column<S extends Schema<S>, T> {
default String encode(final T value) {
return value.toString();
}
}
public interface Schema<S extends Schema<S>> {
List<Column<S, ?>> getColumns();
}
public static class MyData<S extends Schema<S>> {
public <T> T get(final Column<S, T> column) {
return (T) new Integer(164); // actual implementation left out
}
}
public static class MySchema implements Schema<MySchema> {
public static final Column<MySchema, Integer> ID = new Column<MySchema, Integer>(){};
@Override
public List<Column<MySchema, ?>> getColumns() {
return Collections.singletonList(ID);
}
}
public static void main(final String a[]) {
final MySchema s = new MySchema();
final MyData<MySchema> d = new MyData<>();
System.out.println("encoded identifier: " + MySchema.ID.encode(d.get(MySchema.ID)));
for (final Column<MySchema, ?> column : s.getColumns()) {
System.out.println("encoded identifier: " + column.encode(d.get(column)));
}
}
}
答案 0 :(得分:1)
You can create a helper function with a generic parameter T
which allows you to have a Column<T>
instead of a Column<?>
:
public static void main(final String a[]) {
final MySchema s = new MySchema();
final MyData<MySchema> d = new MyData<>();
for (final Column<MySchema, ?> column : s.getColumns())
encode(column, d);
}
private static <T> void encode(Column<T> column, MyData<MySchema> d) {
System.out.println("encoded identifier: " + column.encode(d.get(column)));
}
Another possibility is to use the same trick and provide a convenience method MyData.getEncoded
:
public static class MyData<S extends Schema<S>> {
public <T> T get(final Column<S, T> column) {...}
public <T> String getEncoded(final Column<S, T> column) {
return column.encode(get(column));
}
}