将类型泛化为 Java 中的类型系统
Generelize a type to a system of types in Java
我有一些代码,其中有 Event 这样的对象,它有一个 Attributes 的集合,它的值任何人都可以通过调用事件方法获取:Double getAttributeValue(Integer index)。
直到现在,方法签名中的 AtributeValue 始终是 Double。现在它可以是其他新的 Classes,我需要概括代码,以便在任何地方应用 Double 的任何运算符都应该在我的 classes 新系统上工作。此外,classes 不仅应该像在旧代码中那样操作,一对相同的 class,而且还应该在一个与另一个之间与相同的运算符交互。
例如,旧代码是这样操作的:
private Double calculateDelta(Event event) {
Double firstValue = (Double)event.getAttributeValue(StockEventTypesManager.firstStockMeasurementIndex);
Double secondValue = (Double)event.getAttributeValue(StockEventTypesManager.firstStockMeasurementIndex + 1);
return Math.abs(firstValue - secondValue);
}
新代码应该像这样运行:
private AttributeValue calculateDelta(Event event) {
AttributeValue firstValue = (AttributeValue )event.getAttributeValue(StockEventTypesManager.firstStockMeasurementIndex);
AttributeValue secondValue = (AttributeValue )event.getAttributeValue(StockEventTypesManager.firstStockMeasurementIndex + 1);
return Math.abs(AttrValueFunctional.minus(firstValue,secondValue));
}
或类似的东西(这些只是建议,因为我不知道如何设计它):
private AttributeValue calculateDelta(Event event) {
AttributeValue firstValue = (AttributeValue )event.getAttributeValue(StockEventTypesManager.firstStockMeasurementIndex);
AttributeValue secondValue = (AttributeValue )event.getAttributeValue(StockEventTypesManager.firstStockMeasurementIndex + 1);
return (firstValue.minus(secondValue)).abs();
}
其中此代码可以对应于这些语句对中的每一对:
boolean isDoubleWrapper = firstValue isinstanceof DoubleWrapper; //true
boolean isDoubleList = secondValue isinstanceof DoublePairsList; //true
boolean isDoubleList = firstValue isinstanceof DoublePairsList; //true
boolean isDoubleWrapper = secondValue isinstanceof DoubleWrapper; //true
boolean isDoubleWrapper = firstValue isinstanceof DoubleWrapper; //true
boolean isDoubleWrapper = secondValue isinstanceof DoubleWrapper; //true
boolean isDoublePairsList = firstValue isinstanceof DoublePairsList; //true
boolean isDoublePairsList = secondValue isinstanceof DoublePairsList; //true
也可以是:
boolean isStrangeObject = firstValue isinstanceof StrangeObject; //true
boolean isDoubleList = secondValue isinstanceof DoublePairsList; //true
boolean isDoubleList = firstValue isinstanceof DoublePairsList; //true
boolean isStrangeObject = secondValue isinstanceof StrangeObject; //true
boolean isStrangeObject = firstValue isinstanceof StrangeObject; //true
boolean isStrangeObject = secondValue isinstanceof StrangeObject; //true
boolean isDoublePairsList = firstValue isinstanceof DoublePairsList; //true
boolean isDoublePairsList = secondValue isinstanceof DoublePairsList; //true
我的伟大设计目标是让任何编码员在未来轻松添加一些新的 classes 可以 "hide" 在 AttributeValue 中,添加所需的操作功能(加,减,腹肌),
所以新 classes 应该有很好的可扩展性。
我尝试了一些实现,其中一个通常有效(如下所示),但我认为它没有遵循最好的 java 设计模式,或者它具有良好的可扩展性和易于添加新的 class我提到过。
public class Main {
public interface Operand {}
public interface Combiner<T> {
T subtract(T a, T b);
}
public static abstract class MyFunctional {
public static<T extends Operand> T
compute(Operand a, Operand b, Subtracter combiner) {
return (T) combiner.subtract(a, b);
}
private static A subtractAA(A a, A b){
return new A(a.getFirst() - b.getFirst(), a.getSecond()-b.getSecond());
}
private static A subtractAB(A a, B b){
return new A(a.getFirst() - b.getFirst(), a.getSecond()-b.getSecond()-b.getThird());
}
static class
Subtracter implements Combiner<Operand> {
@Override
public Operand subtract(Operand x, Operand y) {
if(x instanceof A && y instanceof A){
return subtractAA((A)x,(A)y);
}
if(x instanceof A && y instanceof B){
return subtractAB((A)x,(B)y);
}
return null;
}
}
}
public static class A implements Operand{
private int a;
private int b;
public A(int a, int b){
this.a=a;
this.b=b;
}
public int getFirst(){
return a;
}
public int getSecond() {
return b;
}
@Override
public String toString() {
return "("+a+" "+b+")";
}
}
public static class B implements Operand {
private int a;
private int b;
private int c;
public B(int a, int b, int c){
this.a=a;
this.b=b;
this.c = c;
}
public int getFirst(){
return a;
}
public int getSecond() {
return b;
}
public int getThird() {
return b;
}
@Override
public String toString() {
return "("+a+" "+b+" "+c+")";
}
}
public static void main(String[] args) {
Operand e = new A(1, 2);
Operand f = new A(3,4);
Operand g = new B(3,4,5);
System.out.println("Hello World");
System.out.println((Object)MyFunctional.compute(e,f, new MyFunctional.Subtracter()));
Operand o = MyFunctional.compute(f,g, new MyFunctional.Subtracter());
System.out.println("Bye");
}
}
这是我尝试做的一个小例子。
任何人都可以尝试更改我的代码或为那些简单的 A、B classes 建议他自己的代码,这将很容易适应新的 classes(例如,如果想要添加一些简单的 class C 有 4 个字段,以及 C 和它自身之间以及 A 和 C 以及 B 和 C 之间的加法和减法运算),这将按照我在这里和乞讨中的描述工作(实际上几乎相同)。
回答几个问题后编辑:
新类型的实域:
所以在老问题中,Double 类型意味着一个确定性的值(这意味着这个值在现实世界中已知的概率为 1.0)。
现在我需要用图片中的类型系统替换它。
现在关于操作和操作数功能:
同一个分支中的每两个对象都应该作为它自己的第一个祖先来操作。
不同分支的两个对象应该像它们的第一个祖先一样使用相同的操作。
所以最终我想将代码重构为新的假设:
event.getAttributeValue(index);
可以 return 描述的可能层次结构中的任何 class。
当然,现在我需要编写一段代码,只实现最左边的两个对象以及它们之间的所有操作,如减法、加法等。
我的目标是为这个问题设计正确的接口框架和 classes。
根据上次更新的答案再次澄清:
我想做的是某种包装器和功能作为策略设计模式,如以下链接中的两个代码所示:
first(不是模板化示例,我需要根据最后更新的答案进行概括):
first
second(类似这样,但根据建议的解决方案使用 Class、Class 以及 BiFunction 和 BinaryOperator):
second
已经有一个class:BinaryOperator。
忘掉像 AttributeValue 这样的包装器 class。只需让 Event 像以前一样继续保存简单值,包括 Doubles,并将组合操作作为 BinaryOperator 传递。
由于类型擦除,转换为通用类型(如 T
)是不安全的操作,因此您也需要传递类型:
private <V> V calculateDelta(Event event,
Class<V> valueType,
BinaryOperator<V> operation) {
V firstValue = valueType.cast(
event.getAttributeValue(
StockEventTypesManager.firstStockMeasurementIndex));
V secondValue = valueType.cast(
event.getAttributeValue(
StockEventTypesManager.firstStockMeasurementIndex + 1));
return operation.apply(firstValue, secondValue);
}
对该方法的调用如下所示:
Double delta =
calculateDelta(eventToProcess, Double.class, (a, b) -> a - b);
更新:
您在评论中指出操作数并不总是同一类型。在这种情况下,您将使用 BiFunction 而不是 BinaryOperator:
private <V, U, R> R calculateResult(Event event,
Class<V> firstValueType,
Class<U> secondValueType,
BiFunction<V, U, R> operation) {
V firstValue = firstValueType.cast(
event.getAttributeValue(
StockEventTypesManager.firstStockMeasurementIndex));
U secondValue = secondValueType.cast(
event.getAttributeValue(
StockEventTypesManager.firstStockMeasurementIndex + 1));
return operation.apply(firstValue, secondValue);
}
但是,事实证明你可以两者兼顾!您可以使用上述方法,也可以使用第一个版本作为方便,用于您希望两个值和结果具有相同类型的情况:
private <V> V calculateResult(Event event,
Class<V> valueType,
BinaryOperator<V> operation) {
return calculateResult(event, valueType, valueType, operation);
}
说明:
BiFunction 和 BinaryOperator 都是函数式接口——也就是说,只有一个抽象方法的接口。 (static
和 default
方法不是抽象方法。)
这意味着可以使用 lambda 来表示它。 (a, b) -> a - b)
与此相同:
new BinaryOperator<Double>() {
@Override
public Double apply(Double a, Double b) {
return a - b;
}
}
所以,lambda 实际上是 BiFunction 或 BinaryOperator。
它不一定是 lambda。也可以是方法参考:
private Double subtract(Double a, Double b) {
return a - b;
}
// ...
Double delta =
calculateResult(eventToProcess, Double.class, this::subtract);
当然,您可以使这种方法更复杂:
private Double probabilityFor(DiscreteDistribution d, Integer x) {
Map<Integer, Double> probabilities = d.getProbabilities();
return probabilities.getOrDefault(x, 0);
}
// ...
Double probability =
calculateResult(eventToProcess,
DiscreteDistribution.class,
Integer.class,
this::probabilityFor);
您还可以定义对复杂对象本身的操作:
public class DiscreteDistribution {
private final Map<Integer, Double> probabilities = new HashMap<>();
// ...
public Double probabilityOf(int x) {
return probabilities.getOrDefault(x, 0);
}
}
public class ValueCalculator {
// ...
Double probability =
calculateResult(eventToProcess,
DiscreteDistribution.class,
Integer.class,
DiscreteDistribution::probabilityOf);
}
我有一些代码,其中有 Event 这样的对象,它有一个 Attributes 的集合,它的值任何人都可以通过调用事件方法获取:Double getAttributeValue(Integer index)。 直到现在,方法签名中的 AtributeValue 始终是 Double。现在它可以是其他新的 Classes,我需要概括代码,以便在任何地方应用 Double 的任何运算符都应该在我的 classes 新系统上工作。此外,classes 不仅应该像在旧代码中那样操作,一对相同的 class,而且还应该在一个与另一个之间与相同的运算符交互。 例如,旧代码是这样操作的:
private Double calculateDelta(Event event) {
Double firstValue = (Double)event.getAttributeValue(StockEventTypesManager.firstStockMeasurementIndex);
Double secondValue = (Double)event.getAttributeValue(StockEventTypesManager.firstStockMeasurementIndex + 1);
return Math.abs(firstValue - secondValue);
}
新代码应该像这样运行:
private AttributeValue calculateDelta(Event event) {
AttributeValue firstValue = (AttributeValue )event.getAttributeValue(StockEventTypesManager.firstStockMeasurementIndex);
AttributeValue secondValue = (AttributeValue )event.getAttributeValue(StockEventTypesManager.firstStockMeasurementIndex + 1);
return Math.abs(AttrValueFunctional.minus(firstValue,secondValue));
}
或类似的东西(这些只是建议,因为我不知道如何设计它):
private AttributeValue calculateDelta(Event event) {
AttributeValue firstValue = (AttributeValue )event.getAttributeValue(StockEventTypesManager.firstStockMeasurementIndex);
AttributeValue secondValue = (AttributeValue )event.getAttributeValue(StockEventTypesManager.firstStockMeasurementIndex + 1);
return (firstValue.minus(secondValue)).abs();
}
其中此代码可以对应于这些语句对中的每一对:
boolean isDoubleWrapper = firstValue isinstanceof DoubleWrapper; //true
boolean isDoubleList = secondValue isinstanceof DoublePairsList; //true
boolean isDoubleList = firstValue isinstanceof DoublePairsList; //true
boolean isDoubleWrapper = secondValue isinstanceof DoubleWrapper; //true
boolean isDoubleWrapper = firstValue isinstanceof DoubleWrapper; //true
boolean isDoubleWrapper = secondValue isinstanceof DoubleWrapper; //true
boolean isDoublePairsList = firstValue isinstanceof DoublePairsList; //true
boolean isDoublePairsList = secondValue isinstanceof DoublePairsList; //true
也可以是:
boolean isStrangeObject = firstValue isinstanceof StrangeObject; //true
boolean isDoubleList = secondValue isinstanceof DoublePairsList; //true
boolean isDoubleList = firstValue isinstanceof DoublePairsList; //true
boolean isStrangeObject = secondValue isinstanceof StrangeObject; //true
boolean isStrangeObject = firstValue isinstanceof StrangeObject; //true
boolean isStrangeObject = secondValue isinstanceof StrangeObject; //true
boolean isDoublePairsList = firstValue isinstanceof DoublePairsList; //true
boolean isDoublePairsList = secondValue isinstanceof DoublePairsList; //true
我的伟大设计目标是让任何编码员在未来轻松添加一些新的 classes 可以 "hide" 在 AttributeValue 中,添加所需的操作功能(加,减,腹肌), 所以新 classes 应该有很好的可扩展性。
我尝试了一些实现,其中一个通常有效(如下所示),但我认为它没有遵循最好的 java 设计模式,或者它具有良好的可扩展性和易于添加新的 class我提到过。
public class Main {
public interface Operand {}
public interface Combiner<T> {
T subtract(T a, T b);
}
public static abstract class MyFunctional {
public static<T extends Operand> T
compute(Operand a, Operand b, Subtracter combiner) {
return (T) combiner.subtract(a, b);
}
private static A subtractAA(A a, A b){
return new A(a.getFirst() - b.getFirst(), a.getSecond()-b.getSecond());
}
private static A subtractAB(A a, B b){
return new A(a.getFirst() - b.getFirst(), a.getSecond()-b.getSecond()-b.getThird());
}
static class
Subtracter implements Combiner<Operand> {
@Override
public Operand subtract(Operand x, Operand y) {
if(x instanceof A && y instanceof A){
return subtractAA((A)x,(A)y);
}
if(x instanceof A && y instanceof B){
return subtractAB((A)x,(B)y);
}
return null;
}
}
}
public static class A implements Operand{
private int a;
private int b;
public A(int a, int b){
this.a=a;
this.b=b;
}
public int getFirst(){
return a;
}
public int getSecond() {
return b;
}
@Override
public String toString() {
return "("+a+" "+b+")";
}
}
public static class B implements Operand {
private int a;
private int b;
private int c;
public B(int a, int b, int c){
this.a=a;
this.b=b;
this.c = c;
}
public int getFirst(){
return a;
}
public int getSecond() {
return b;
}
public int getThird() {
return b;
}
@Override
public String toString() {
return "("+a+" "+b+" "+c+")";
}
}
public static void main(String[] args) {
Operand e = new A(1, 2);
Operand f = new A(3,4);
Operand g = new B(3,4,5);
System.out.println("Hello World");
System.out.println((Object)MyFunctional.compute(e,f, new MyFunctional.Subtracter()));
Operand o = MyFunctional.compute(f,g, new MyFunctional.Subtracter());
System.out.println("Bye");
}
}
这是我尝试做的一个小例子。 任何人都可以尝试更改我的代码或为那些简单的 A、B classes 建议他自己的代码,这将很容易适应新的 classes(例如,如果想要添加一些简单的 class C 有 4 个字段,以及 C 和它自身之间以及 A 和 C 以及 B 和 C 之间的加法和减法运算),这将按照我在这里和乞讨中的描述工作(实际上几乎相同)。
回答几个问题后编辑:
新类型的实域:
所以在老问题中,Double 类型意味着一个确定性的值(这意味着这个值在现实世界中已知的概率为 1.0)。
现在我需要用图片中的类型系统替换它。
所以最终我想将代码重构为新的假设:
event.getAttributeValue(index);
可以 return 描述的可能层次结构中的任何 class。 当然,现在我需要编写一段代码,只实现最左边的两个对象以及它们之间的所有操作,如减法、加法等。 我的目标是为这个问题设计正确的接口框架和 classes。
根据上次更新的答案再次澄清:
我想做的是某种包装器和功能作为策略设计模式,如以下链接中的两个代码所示:
first(不是模板化示例,我需要根据最后更新的答案进行概括): first
second(类似这样,但根据建议的解决方案使用 Class、Class 以及 BiFunction 和 BinaryOperator): second
已经有一个class:BinaryOperator。
忘掉像 AttributeValue 这样的包装器 class。只需让 Event 像以前一样继续保存简单值,包括 Doubles,并将组合操作作为 BinaryOperator 传递。
由于类型擦除,转换为通用类型(如 T
)是不安全的操作,因此您也需要传递类型:
private <V> V calculateDelta(Event event,
Class<V> valueType,
BinaryOperator<V> operation) {
V firstValue = valueType.cast(
event.getAttributeValue(
StockEventTypesManager.firstStockMeasurementIndex));
V secondValue = valueType.cast(
event.getAttributeValue(
StockEventTypesManager.firstStockMeasurementIndex + 1));
return operation.apply(firstValue, secondValue);
}
对该方法的调用如下所示:
Double delta =
calculateDelta(eventToProcess, Double.class, (a, b) -> a - b);
更新:
您在评论中指出操作数并不总是同一类型。在这种情况下,您将使用 BiFunction 而不是 BinaryOperator:
private <V, U, R> R calculateResult(Event event,
Class<V> firstValueType,
Class<U> secondValueType,
BiFunction<V, U, R> operation) {
V firstValue = firstValueType.cast(
event.getAttributeValue(
StockEventTypesManager.firstStockMeasurementIndex));
U secondValue = secondValueType.cast(
event.getAttributeValue(
StockEventTypesManager.firstStockMeasurementIndex + 1));
return operation.apply(firstValue, secondValue);
}
但是,事实证明你可以两者兼顾!您可以使用上述方法,也可以使用第一个版本作为方便,用于您希望两个值和结果具有相同类型的情况:
private <V> V calculateResult(Event event,
Class<V> valueType,
BinaryOperator<V> operation) {
return calculateResult(event, valueType, valueType, operation);
}
说明:
BiFunction 和 BinaryOperator 都是函数式接口——也就是说,只有一个抽象方法的接口。 (static
和 default
方法不是抽象方法。)
这意味着可以使用 lambda 来表示它。 (a, b) -> a - b)
与此相同:
new BinaryOperator<Double>() {
@Override
public Double apply(Double a, Double b) {
return a - b;
}
}
所以,lambda 实际上是 BiFunction 或 BinaryOperator。
它不一定是 lambda。也可以是方法参考:
private Double subtract(Double a, Double b) {
return a - b;
}
// ...
Double delta =
calculateResult(eventToProcess, Double.class, this::subtract);
当然,您可以使这种方法更复杂:
private Double probabilityFor(DiscreteDistribution d, Integer x) {
Map<Integer, Double> probabilities = d.getProbabilities();
return probabilities.getOrDefault(x, 0);
}
// ...
Double probability =
calculateResult(eventToProcess,
DiscreteDistribution.class,
Integer.class,
this::probabilityFor);
您还可以定义对复杂对象本身的操作:
public class DiscreteDistribution {
private final Map<Integer, Double> probabilities = new HashMap<>();
// ...
public Double probabilityOf(int x) {
return probabilities.getOrDefault(x, 0);
}
}
public class ValueCalculator {
// ...
Double probability =
calculateResult(eventToProcess,
DiscreteDistribution.class,
Integer.class,
DiscreteDistribution::probabilityOf);
}