支持实现接口

时间:2017-11-19 12:47:06

标签: java generics design-patterns interface

我有一个界面Operations

public interface Operations{

    Operations add(Operations op);

    Operations subtract(Operations op);
}

此界面应允许用户选择如何进行简单计算。例如,如果用户只想使用数据类型Integer及其基本操作,则可以使用类MyInteger

// simple implementation without type check etc..
public final class MyInteger implements Operations{
    private final int delegate;

    public MyInteger(int i){
        delegate = i;
    }

    @Override
    Operations add(Operations other){
        return new MyInteger(delegate + ((MyInteger) other).delegate);
    }
    @Override
    Operations subtract(Operations other){
        return new MyInteger(delegate - ((MyInteger) other).delegate);
    }
    (...)
}

在其他类中,将根据接口Operations的功能进行计算。但有时我需要一个静态上下文,如valueOf(val)函数,它返回给定String,double,int等对应类的实现,如下例所示:

public class Calculation<D extends Operations> {

    private final D value1, value2;
    private final D value3 = D.valueOf(4);  // cannot implement this in interface 

    public Calculation(D value1, D value2){
        this.value1 =value1;
        this.value2 = value2;
    }

    public D sumPlusValue3(){
        return value1.plus(value2).plus(value3);
    }
}

所以

Calculation<MyInteger> calc = new Calculation<MyInteger>(new MyInteger(1), new MyInteger(2));
Operations result = calc.sumPlusValue3();

一般来说,我认为我必须使用BuilderFactory之类的设计模式来允许用户指定他想要的实现,例如

Operations integerImpl = OperationsBuilder.create("Integer");

但是在研究了这些模式之后,我找不到一种干净的方法来在D类中为value3获得泛型类Calculation的实现。

编辑1

我编辑了这个问题并标记了来自@Stefan Haustein的答案,因为它是我问题的最佳解决方案。 但是@davidxxx的答案对于我的初始问题也是一个很好的答案 - 如何表示 valueOf(int val)函数。

2 个答案:

答案 0 :(得分:2)

1)初始问题:工厂调用

这里:

private final D value3 = D.valueOf(4);  // cannot implement this in interface 

您希望值value3具有非工厂的通用。
在接口中定义valueOf()(以及实例方法)不是解决方案。

作为替代方案,您可以在Calculation构造函数中引入工厂参数,以便有机会估算value3

所以替换:

private final D value3 = D.valueOf(4);

public Calculation(D value1, D value2){
    this.value1 =value1;
    this.value2 = value2;
}

by:

private final D value3; 

public Calculation(D value1, D value2, FactoryD factoryD){
    this.value1 =value1;
    this.value2 = value2;
    this.value3 = factoryD.create(4);
}

或更好地使用工厂的Function功能界面:

private final D value3; 

public Calculation(D value1, D value2, IntFunction<D> factoryD){
    this.value1 =value1;
    this.value2 = value2;
    this.value3 = factoryD.apply(4);
}

你可以这样使用它:

Calculation<MyInteger> calc = new Calculation<>(new MyInteger(1), new MyInteger(2), MyInteger::new );
Operations result = calc.sumPlusValue3();

但是Operations错过了一些东西。

2)无法表示Operations值。

实际上,Operations定义了操作方法,但无法显示对象值:

public interface Operations{

    Operations add(Operations op);

    Operations subtract(Operations op);
}

您应该定义一种方法来获取值并使接口通用以处理任何类型的值:

public interface Operations<T> {

    Operations<T> add(Operations<T> op);

    Operations<T> subtract(Operations<T> op);

    T getValue();

}

MyInteger可以定义为:

public final class MyInteger implements Operations<Integer> {

    private final int delegate;

    public MyInteger(int i) {
      delegate = i;
    }

    @Override
    public Operations<Integer> add(Operations<Integer> other) {
      return new MyInteger(delegate + other.getValue());
    }

    @Override
    public Operations<Integer> subtract(Operations<Integer> other) {
      return new MyInteger(delegate - other.getValue());
    }

    @Override
    public Integer getValue() {
      return delegate;
    }
}

Calculation可以使用两个泛型来定义:一个用于Operations,另一个用于Operations返回类型:

public class Calculation<C, D extends Operations<C>> {

    private final D value1, value2;
    private final D value3; 

    public Calculation(D value1, D value2, IntFunction<D> factoryD){
        this.value1 =value1;
        this.value2 = value2;
        this.value3 = factoryD.apply(4);
    }

    public Operations<C> sumPlusValue3(){
        return value1.add(value2).add(value3);
    }        
}

您可以这样使用此代码:

Calculation<Integer, MyInteger> calc = new Calculation<>(new MyInteger(1), new MyInteger(2), MyInteger::new );
Operations<Integer> result = calc.sumPlusValue3();
System.out.println(result.getValue());

输出:

  

7

答案 1 :(得分:1)

如果在Calculation中你总是有一个D附近的实例,你可以让valueOf成为Operations的非静态成员,尽管从技术上来说你不会&#39} ; t需要相应的值。

如果使用&#34;虚拟&#34; valueOf的实例不是一个选项,您可能需要Class<D>作为Calculations的构造函数参数。这将允许您调用newInstance(),也许提供一个中性元素 - 但您仍然需要addInt之类的操作才能获得具体值。可以将这些想法结合起来,以避免需要明确的虚拟值。

另一种选择是在Calculation中使valueOf为abstract,避免对虚拟实例的依赖,将负担转移到Calculation实例创建:

Calculation<MyInteger> calc = new Calculation<MyInteger>(
    new MyInteger(1), new MyInteger(2)) {
      public MyInteger valueOf(int i) {
        return new MyInteger(i);
      }
    });

Operations result = calc.sumPlusValue3();

一般来说,这似乎是关于&#34;纯度&#34;之间的正确平衡。和&#34;额外的层/复杂性&#34;。我倾向于更加务实,避免在这里花费 - 以牺牲纯度为代价。

P.S。另一种选择可能是拥有一个显式的工厂接口,但可以将其作为约定存储为Operations类的字段:

public final class MyInteger implements Operations{
  private final int delegate;

  public static final OperationsFactory<MyInteger> FACTORY = 
    new OperationsFactory<>() {
      @Override
      MyInteger valueOf(int i) {
        return new MyInteger(i);
      }
    };

  public MyInteger(int i){
      delegate = i;
  }

  @Override
  Operations add(Operations other){
      return new MyInteger(delegate + ((MyInteger) other).delegate);
  }
  @Override
  Operations subtract(Operations other){
    return new MyInteger(delegate - ((MyInteger) other).delegate);
  }
  (...)
}

这至少可以将所有内容的实现方保存在一个文件中......