最后的路障与建设者

时间:2011-06-07 00:23:36

标签: java immutability builder final

我有一个使用构建器(J Bloch样式)的图(g)。需要反转图表以运行某些统计信息,然后对报告和分析算法进行高速缓存以进行访问。

因此,图g定义了以下参考变量:

private final Builder savedBuilder; // save builder for clone build with same properties.
private final Graph   gPrime;       // must reverse populated graphs BEFORE cache of stats

注意:gPrime指的是相同的图形,除了它是从所显示的g中填充的。       gPrime.gPrime应该引用g,因为g与gPrime相反。

和构建器构建方法:

public Graph build() {
  Graph g = new Graph(this);
  g.gPrime.gPrime = g;
  return g;
}

以及构建器的构造函数:

private Graph (Builder builder){  // 'this' used for clarity
  this.gType        = builder.gType;
  this.dropOrphans  = builder.dropOrphans;
  this.fileHandle   = builder.fileHandle;       
  this.nodes        = builder.nodes;            
  this.edges        = builder.edges;            
  this.delimiter    = builder.delimiter;
  this.mapCapacity  = builder.mapCapacity;       
  this.mapLoadFactor    = builder.mapLoadFactor;
  this.savedBuilder = builder;  // save builder for cloning

  emptyGraph();     // build empty structure for data in this graph
  if (this.fileHandle == null) {           // no file data
    if (this.nodes == 0) {         // no sizing info
    ;                  // nothing else to do - - - empty graph
    } else {                    // we have # of nodes
      if ( this.edges == 0) {            // just an edge-less integer graph)
      populateEdgeless(nodes)   ;
      } else {                 // randomly generated graph
    populateRandom(nodes, edges);
      }
    }
  } else {                 // populate from file
    populateFromFile();
  }

  // To create empty graph to transpose our values into,
  // we need to clear out builder settings that would populate a new graph.
  savedBuilder.fileHandle = null;
  savedBuilder.nodes = 0;
  savedBuilder.edges = 0;

  // otherwise, everything the same, so just pass modified builder to constructor
  // save the reference to this graph ( ready for the reversal method to use )
  this.gPrime = new Graph(savedBuilder);

)

再一次。目标是两个图形对象,每个图形对象都引用另一个图形对象。

顺序是: 构建g - - - 填充g - - - 将g反转为具有与g

相同特征的空图

所以,这是我不太明白的问题。

如果我将g分配给gPrime.gPrime,无论是在我们构建之后的构建中还是在构造函数的底部,我都会收到一条错误消息,指出gPrime是最终的。 Eclipse表明它是第一个有问题的gPrime - - - 这是真的 - - 它是最终的并且已被分配。但gPrime.gprime(强调第二个gPrime)尚未分配。 (我搜索了整个程序。)

我也尝试将赋值放在反向方法的底部。同样的事情。

我还在构建器中尝试了g.gPrime.gPrime。同样的事情。

这几乎就好像编译器对哪个gPrime正在接收作业感到困惑。

我确信有些东西我没有看到或理解 - 但 - - - - 不知道如何实现这一目标。

如果我拿出决赛,我可以做到这一点,但我想要变成永恒的。

3 个答案:

答案 0 :(得分:2)

您需要不可变的循环依赖。您必须实现它,以便在Build A(在A的构造函数中)时,您必须使用constructor调用B this

以下是 Builders 的代码(您必须确保整个构建过程不会从当前线程中逃脱):

public class A {

  private final B b_;
  private final String name_;

  private A(Builder b) {
    b_ = b.bB_.a(this).build();
    name_ = b.name_;
  }

  public String name() {
    return name_;
  }

  public B b() {
    return b_;
  }

  @Override
  public String toString() {
    return "[" + name_ + ": " + b_.name() + " ]";
  }

  public static class Builder {

    private B.Builder bB_;
    private String name_;

    public Builder bB(B.Builder bB) {
      bB_ = bB;
      return this;
    }

    public Builder name(String arg) {
      name_ = arg;
      return this;

    }

    public A build() {
      return new A(this);
    }
  }

}

B组:

public class B {

  private final A a_;
  private final String name_;

  private B(Builder b) {
    a_ = b.a_;
    name_ = b.name_;
  }

  public String name() {
    return name_;
  }

  @Override
  public String toString() {
    return "[" + name_ + ": " + a_.name() + " ]";
  }

  public static class Builder {

    private A a_;
    private String name_;

    public Builder a(A a) {
      a_ = a;
      return this;

    }

    public Builder name(String arg) {
      name_ = arg;
      return this;

    }

    public B build() {
      return new B(this);
    }
  }

}

如何使用它:

public class Main {

  public static void main(String[] args) {
    A.Builder aBl = new A.Builder().name("I am A1");
    B.Builder bBl = new B.Builder().name("I am B1");

    A a = aBl.bB(bBl).build();

    System.out.println(a);
    System.out.println(a.b());

  }

}

^
^
^
如果您想要循环依赖中的单个类和两个对象)

public class A {

  private final A other_;
  private final String name_;

  private A(Builder b) {
    if (b.otherBulder_ != null) {
      other_ = b.otherBulder_.otherInstance(this).build();
    } else {
      other_ = b.otherInstance_;
    }
    name_ = b.name_;
  }

  @Override
  public String toString() {
    return "[" + name_ + ": " + other_.name() + " ]";
  }

  public String name() {
    return name_;
  }

  public A other() {
    return other_;
  }

  static class Builder {

    private Builder otherBulder_;
    private A otherInstance_;
    private String name_;

    Builder name(String name) {
      name_ = name;
      return this;
    }

    Builder otherBuilder(Builder other) {
      otherBulder_ = other;
      return this;
    }

    Builder otherInstance(A instance) {
      otherInstance_ = instance;
      return this;
    }

    A build() {
      return new A(this);
    }
  }

  public static void main(String[] args) {
    Builder a1B = new Builder().name("A1");
    Builder a2B = new Builder().name("A2");

    A a = a1B.otherBuilder(a2B).build();
    System.out.println(a);
    System.out.println(a.other());

  }
}

答案 1 :(得分:1)

我认为我的答案只是Op De Cirkel答案的简化版本,可根据您的具体需求量身定制。我的想法是在构建器中包含gPrime实例,因此在构造函数中,如果构建器具有非null的gPrime,请使用它,否则构建它:

private Graph(Builder builder){


  /* .... setup code omitted....*/

  if (builder.gPrime == null){
     savedBuilder.gPrime = this;
     this.gPrime = new Graph(savedBuilder);
  }else{
     this.gPrime = builder.gPrime;
  }

}

this.gPrime.gPrime = this无法在任何地方工作,因为gPrime是实例最终字段,只能在声明时或在该实例的构造函数中初始化。 this.gPrime.gPrime = this正在做的是初始化其他实例的最终字段,这违反了gPrime

的“最终性”

答案 2 :(得分:0)

您无法初始化不可变的循环结构。

通过将gPrime更改为final并确保在使用之前始终将其设置为值,您可以将其有效地保持不变。

感谢Op De Cirkel的回答:

您可以在构建器的功能中使用两个构建器或开关,其中您有一个构建器,其中包含对您当前构建的Graph对象的引用:

public class Graph {

    private final Graph gPrime;
    private final String name_;

    private Graph(PrimeBuilder b) {
        gPrime = b.g;
        name_ = b.name_;
    }

    private Graph(Builder b) {
        gPrime = new PrimeBuilder(this).name("gPrime").build();
        name_ = b.name_;
    }

    public String name() {
        return name_;
    }

    public Graph gPrime() {
        return gPrime;
    }

    @Override
    public String toString() {
        return "I am " + name_ + ", my gPrime is " + gPrime.name();
    }

    public static class PrimeBuilder {

        private Graph g;
        private String name_;

        public PrimeBuilder(Graph g) {
            this.g = g;
        }

        public PrimeBuilder name(String arg) {
            name_ = arg;
            return this;

        }

        public Graph build() {
            return new Graph(this);
        }
    }

    public static class Builder {

        private String name_;

        public Builder name(String arg) {
            name_ = arg;
            return this;
        }

        public Graph build() {
            return new Graph(this);
        }
    }

}

用法示例:

public class Main {

  public static void main(String[] args) {
    Graph g = new Graph.Builder().name("g").build();

    System.out.println(g);
    System.out.println(g.gPrime());

  }

}