如何解决原始对象(如 Integer、String)不允许派生的限制?
How to work-around the restriction of primitive objects - like Integer, String - not being allowed to derive from?
我意识到无法从原始对象派生,因为它们被声明为 final。我该如何解决这个限制?我正在使用 JPA 标准 API 进行编程。几乎所有我处理的地方都使用我自己的方法,这些方法具有 Integer/String 参数来与表示数据库 table 行值的实体字段进行比较。对于这些参数中的任何一个,我都愿意接受 QueryParameter<Integer>
或 QueryParameter<String>
。这样做我将不得不第二次创建该方法来接受查询参数而不是文字。但是,考虑具有置换文字和查询参数的值列表(如 QueryBuilder 的 in(...) 方法),很难甚至不可能实现。
让我们假设我有一个实体 Car
和一个方法 withFeatures(StringRepresentation ... features)
并且会有文字和查询参数派生自相同的 super-class StringRepresentation
它本身是从原始类型 String 派生的。我愿意这样做:
myCar.withFeatures("Seat Heating", "Metallic Color", "Trailer Hitch");
myCar.withFeatures(new QueryParam<String>("MyFavourit"));
myCar.withFeatures("Seat Heating", new QueryParam<String>("LoveThatColor"), "Trailer Hitch");
有没有人对此有方法或解决方案?
我会针对每种类型的标准使用一种方法的构建器模式。
class Car {
private Set<String> features = new HashSet();
public Car withFeature(String f) {
features.add(f);
return this;
}
public Car withFeature(QueryParameter<String> q) {
features.add(q.getStringRepresentation()); // or whatever
return this;
}
...
}
所以你可以说:
myCar.withFeature("Seat Heating")
.withFeature(new QueryParam<String>("MyFavourit");
with permutating literals and query parameters, makes it hard or even impossible to implement
您可以将 CharSequence
用于字符串,但我不确定这是个好主意...
import lombok.RequiredArgsConstructor;
public class Test {
public static void main(String[] args) {
withFeatures("test", new StringQueryParam("test2"));
}
@SafeVarargs
public final static <T extends CharSequence> void withFeatures(T ...params) {
// Wrap in StringQueryParam if not an instance of QueryParam<String>
}
interface QueryParam<T> {
}
@RequiredArgsConstructor
static class StringQueryParam implements QueryParam<String>, CharSequence {
private final CharSequence value;
@Override
public int length() {
return value.length();
}
@Override
public char charAt(int index) {
return value.charAt(index);
}
@Override
public CharSequence subSequence(int start, int end) {
return value.subSequence(start, end);
}
}
}
减少冗长的静态工厂方法(例如,QueryParam.of
、QueryParam.all
等查询参数)与构建器或有效组合它们的方法可能会有所帮助。
例如
// Assuming Lists.union util method
withFeatures(Lists.union(
QueryParam.all("a", "b"),
QueryParam.of("c")
));
// With static imports
withFeatures(union(params("a", "b"), param("c"));
// With ParamsBuilder
withFeatures(ParamsBuilder.of("a", "b").add(QueryParam.of("c").build())));
希望这能给您一些关于如何设计 API 的想法!您也可以使用更复杂但更灵活的路由,其中整个条件只是一个 AST,因此 QueryParam
实际上只是 AST 中的一种 Expression
允许创建复合等。如果您查看 QueryDSL 一切都是 DslExpression
并且您有访问者对树执行操作。
到目前为止,我花了一些时间从 Java 社区获取提示来解决这个问题。
当然,我是 Java 类型安全概念的追随者(感谢 plalx)。因此,我的解决方案可能与参数化类型有关。
而且我也像许多其他人一样欣赏设计模式的概念(感谢 tgdavies)。因此,我将构建器模式与一种方法一起用于每种类型的标准。我将接受为
实施汽车特征方法
- 使用字符串的普通旧文字
- 以及指定字符串参数
即:
myCar.withFeatures("Seat Heating", "Metallic Color", "Trailer Hitch");
以及通过使用静态方法 sp(...)
以稍微复杂的方式指定(比方说)某种类型的查询参数或字符串参数
myCar.withFeatures(sp("MyFavourit"));
当然还有两者的混合,引入另一个用于字符串表示的静态方法 sr(...):
myCar.withFeatures(sr("Seat Heating"), sp("LoveThatColor"), sr("Trailer Hitch"));
在我们想要在方法签名中使用可变参数来指定这些表示的情况下,两者的混合很重要,在本例中为汽车特征。
可以看出,这几乎就是我在发布这个问题时上面所说的用法。
我怎样才能做到这一点?
起初我设计了一个接口来实现我的不同字符串表示:
public interface ValueTypeRepresentation<T> {
public Class<T> getClazz();
public QueryParameter<T> getQueryParameter();
public RepresentationType getRepresentationType();
public T getValue();
}
方法是判断representation是文字还是参数,并分别获取文字的值。参数本身稍后使用它的名称。
clazz 成员是为了简化 Java 通用类型推断的目的,因为我将使用参数化类型来实现不同的类型表示。正如我所说,String 只是节目的开始。
然后我设计了一个抽象 class 来导出不同原始对象的具体 class 表示:
abstract class AbstractValueTypeRepresentation<T> implements ValueTypeRepresentation<T> {
private Class<T> clazz;
private RepresentationType representationType = RepresentationType.VALUE;
private QueryParameter<T> queryParameter;
private T value;
public AbstractValueTypeRepresentation(Class<T> clazz, T value) {
this.clazz = clazz;
this.representationType = RepresentationType.VALUE;
this.value = value;
}
public AbstractValueTypeRepresentation(QueryParameter<T> qp) {
this.clazz = qp.getClazz();
this.representationType = RepresentationType.PARAM;
this.queryParameter = qp;
}
@Override
public Class<T> getClazz() {
return clazz;
}
@Override
public QueryParameter<T> getQueryParameter() {
return queryParameter;
}
@Override
public RepresentationType getRepresentationType() {
return representationType;
}
@Override
public T getValue() {
return value;
}
}
为了区分该类型的文字和该类型的查询参数,我引入了这个枚举:
public enum RepresentationType {
PARAM, VALUE;
}
然后我设计了第一个具体表示,这里是我的StringRepresentation(源自上面的抽象class):
public class StringRepresentation extends AbstractValueTypeRepresentation<String> {
public static StringRepresentation sr(String s) {
return new StringRepresentation(s);
}
public static StringRepresentation sp(String name) {
return new StringRepresentation(new QueryParameter<String>(String.class, name));
}
public StringRepresentation(String value) {
super(String.class, value);
}
public StringRepresentation(QueryParameter<String> queryParameter) {
super(queryParameter);
}
}
显然这很容易扩展到 Integer、Float、LocalDate 等的表示
我意识到无法从原始对象派生,因为它们被声明为 final。我该如何解决这个限制?我正在使用 JPA 标准 API 进行编程。几乎所有我处理的地方都使用我自己的方法,这些方法具有 Integer/String 参数来与表示数据库 table 行值的实体字段进行比较。对于这些参数中的任何一个,我都愿意接受 QueryParameter<Integer>
或 QueryParameter<String>
。这样做我将不得不第二次创建该方法来接受查询参数而不是文字。但是,考虑具有置换文字和查询参数的值列表(如 QueryBuilder 的 in(...) 方法),很难甚至不可能实现。
让我们假设我有一个实体 Car
和一个方法 withFeatures(StringRepresentation ... features)
并且会有文字和查询参数派生自相同的 super-class StringRepresentation
它本身是从原始类型 String 派生的。我愿意这样做:
myCar.withFeatures("Seat Heating", "Metallic Color", "Trailer Hitch");
myCar.withFeatures(new QueryParam<String>("MyFavourit"));
myCar.withFeatures("Seat Heating", new QueryParam<String>("LoveThatColor"), "Trailer Hitch");
有没有人对此有方法或解决方案?
我会针对每种类型的标准使用一种方法的构建器模式。
class Car {
private Set<String> features = new HashSet();
public Car withFeature(String f) {
features.add(f);
return this;
}
public Car withFeature(QueryParameter<String> q) {
features.add(q.getStringRepresentation()); // or whatever
return this;
}
...
}
所以你可以说:
myCar.withFeature("Seat Heating")
.withFeature(new QueryParam<String>("MyFavourit");
with permutating literals and query parameters, makes it hard or even impossible to implement
您可以将 CharSequence
用于字符串,但我不确定这是个好主意...
import lombok.RequiredArgsConstructor;
public class Test {
public static void main(String[] args) {
withFeatures("test", new StringQueryParam("test2"));
}
@SafeVarargs
public final static <T extends CharSequence> void withFeatures(T ...params) {
// Wrap in StringQueryParam if not an instance of QueryParam<String>
}
interface QueryParam<T> {
}
@RequiredArgsConstructor
static class StringQueryParam implements QueryParam<String>, CharSequence {
private final CharSequence value;
@Override
public int length() {
return value.length();
}
@Override
public char charAt(int index) {
return value.charAt(index);
}
@Override
public CharSequence subSequence(int start, int end) {
return value.subSequence(start, end);
}
}
}
减少冗长的静态工厂方法(例如,QueryParam.of
、QueryParam.all
等查询参数)与构建器或有效组合它们的方法可能会有所帮助。
例如
// Assuming Lists.union util method
withFeatures(Lists.union(
QueryParam.all("a", "b"),
QueryParam.of("c")
));
// With static imports
withFeatures(union(params("a", "b"), param("c"));
// With ParamsBuilder
withFeatures(ParamsBuilder.of("a", "b").add(QueryParam.of("c").build())));
希望这能给您一些关于如何设计 API 的想法!您也可以使用更复杂但更灵活的路由,其中整个条件只是一个 AST,因此 QueryParam
实际上只是 AST 中的一种 Expression
允许创建复合等。如果您查看 QueryDSL 一切都是 DslExpression
并且您有访问者对树执行操作。
到目前为止,我花了一些时间从 Java 社区获取提示来解决这个问题。 当然,我是 Java 类型安全概念的追随者(感谢 plalx)。因此,我的解决方案可能与参数化类型有关。 而且我也像许多其他人一样欣赏设计模式的概念(感谢 tgdavies)。因此,我将构建器模式与一种方法一起用于每种类型的标准。我将接受为
实施汽车特征方法- 使用字符串的普通旧文字
- 以及指定字符串参数
即:
myCar.withFeatures("Seat Heating", "Metallic Color", "Trailer Hitch");
以及通过使用静态方法 sp(...)
以稍微复杂的方式指定(比方说)某种类型的查询参数或字符串参数myCar.withFeatures(sp("MyFavourit"));
当然还有两者的混合,引入另一个用于字符串表示的静态方法 sr(...):
myCar.withFeatures(sr("Seat Heating"), sp("LoveThatColor"), sr("Trailer Hitch"));
在我们想要在方法签名中使用可变参数来指定这些表示的情况下,两者的混合很重要,在本例中为汽车特征。 可以看出,这几乎就是我在发布这个问题时上面所说的用法。 我怎样才能做到这一点?
起初我设计了一个接口来实现我的不同字符串表示:
public interface ValueTypeRepresentation<T> {
public Class<T> getClazz();
public QueryParameter<T> getQueryParameter();
public RepresentationType getRepresentationType();
public T getValue();
}
方法是判断representation是文字还是参数,并分别获取文字的值。参数本身稍后使用它的名称。 clazz 成员是为了简化 Java 通用类型推断的目的,因为我将使用参数化类型来实现不同的类型表示。正如我所说,String 只是节目的开始。
然后我设计了一个抽象 class 来导出不同原始对象的具体 class 表示:
abstract class AbstractValueTypeRepresentation<T> implements ValueTypeRepresentation<T> {
private Class<T> clazz;
private RepresentationType representationType = RepresentationType.VALUE;
private QueryParameter<T> queryParameter;
private T value;
public AbstractValueTypeRepresentation(Class<T> clazz, T value) {
this.clazz = clazz;
this.representationType = RepresentationType.VALUE;
this.value = value;
}
public AbstractValueTypeRepresentation(QueryParameter<T> qp) {
this.clazz = qp.getClazz();
this.representationType = RepresentationType.PARAM;
this.queryParameter = qp;
}
@Override
public Class<T> getClazz() {
return clazz;
}
@Override
public QueryParameter<T> getQueryParameter() {
return queryParameter;
}
@Override
public RepresentationType getRepresentationType() {
return representationType;
}
@Override
public T getValue() {
return value;
}
}
为了区分该类型的文字和该类型的查询参数,我引入了这个枚举:
public enum RepresentationType {
PARAM, VALUE;
}
然后我设计了第一个具体表示,这里是我的StringRepresentation(源自上面的抽象class):
public class StringRepresentation extends AbstractValueTypeRepresentation<String> {
public static StringRepresentation sr(String s) {
return new StringRepresentation(s);
}
public static StringRepresentation sp(String name) {
return new StringRepresentation(new QueryParameter<String>(String.class, name));
}
public StringRepresentation(String value) {
super(String.class, value);
}
public StringRepresentation(QueryParameter<String> queryParameter) {
super(queryParameter);
}
}
显然这很容易扩展到 Integer、Float、LocalDate 等的表示