将类型泛化为 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 都是函数式接口——也就是说,只有一个抽象方法的接口。 (staticdefault 方法不是抽象方法。)

这意味着可以使用 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);

}