0
class Some{
  private int id;
  private String name;
  //getters and setters
}
class Check{
  private Some[] someVals;
  //getters and setters
}

假设我已将值填充到 Check 类中的 someVals 中

void newMethod(){
      Check checkPrev = getCheckPopulated();
      Some[] someFirst = checkPrev.getSomeVals();
      modifySome(someFirst);
      Some[] some  = ? // at this point need the values of someFirst
   }

我的问题是即使在修改之后(我已经指定)也要获取 Some[] 数组的值,这是分配时首先出现的值。

好吧,我会把我的问题说清楚。final Some[] someFirst = checkPrev.getSomeVals(); 没有工作通过保留数组值而不必重新将所有值重新分配给另一个数组,是否有类似于 final 的小技巧?

4

5 回答 5

3

你不能一边吃蛋糕一边吃。您必须制作对象的深层副本,然后修改原始副本。然后,深层副本将包含原始值。

于 2013-06-04T15:03:46.970 回答
1

在您的 modifySome 方法中,返回一个新的 Some[] 数组。

Some[] modifySome(Some[] passedArray){
  Some[] result = new Some[passedArray.length];
  System.arraycopy( passedArray, 0, result , 0, a.length );
  //Modify result as needed
  return result
}

如果您可以通过 modifySome 返回一个数组,您的代码可以更改为:

Some[] some = modifySome(someFirst);

在该行之后,someFirst仍将与之前相同,并且some将是修改后的值。

于 2013-06-04T15:03:33.647 回答
0

一种选择是使用CopyOnWriteArrayList

CopyOnWriteArrayList<Some> someFirst = checkPrev.getSomeVals();
Iterator iterator = someFirst.iterator();
modifySome(some);

迭代器仍将引用原始列表,而不是修改后的列表。

另一种选择是制作原始数组的副本。

Some[] someFirst = checkPrev.getSomeVals();
Some[] someCopy = new Some[someFirst.length];
System.arrayCopy(someFirst, 0, someCopy, 0, someFirst.length);
modifySome(some);

someCopy仍将保留原始数组的副本。

于 2013-06-04T15:05:15.183 回答
0

Java 的缺点之一是基本上只有一种非原始类型:混杂堆对象引用。如果一个类的实例在其包之外拥有对类George对象的引用,则无法与外部代码共享该引用,而不赋予该外部代码永久的能力来做任何可以用它做的事情。Java 的部分设计目标是即使在简单的硬件系统上也易于实现,并且具有单一的非原始类型有助于实现该目标。另一方面,这也意味着程序员需要跟踪哪些对象引用用于封装:FooBarFooGeorge

  • 除身份之外的对象状态的不可变方面,即使通过包含引用的代码也无法更改。

  • 对象身份(以及状态的其他不可变方面)

  • 对象状态的各个方面是可变的,除了它们被期望永远不会被赋予实际上会改变它们的代码,而不是身份。

  • 对象状态的可变方面,由持有引用但不持有身份的代码“拥有”。

  • 对象状态的可变方面,以及身份。

在您的代码中,因为数组是可变的,所以您的数组类型字段不能具有第一个含义,但它可以包含其他四个中的任何一个。此外,数组的元素可以包含上述任何一种东西。如果您认为对象的状态是其数组中保存的id和对的组合,如果保存引用的a和/或a可以更改,并且如果此类更改将被视为状态的更改,然后复制 的状态将需要创建一个新数组,并用新实例填充它,这些实例的数据是从原始数组中的相应实例复制而来的。nameidnameSomeCheckCheckCheckSome

如果Check数组中的任何对象都不会暴露给可能改变它们的代码,那么就没有必要构造单个Check对象的新实例;创建一个新数组并使用对原始数组中对象的引用填充它就足够了。同样,如果数组的目的是封装在别处定义的对象的身份Check,因此对这些对象的更改不会被视为对Check状态的更改。请注意,在前一种情况下(对象永远不会改变),用Some保存相同数据的新实例替换对象效率低下,但不会破坏任何东西。在后一种情况下(数组封装了身份对象,而不是它们的状态),用对新实例的引用替换引用会破坏代码。

虽然许多人谈论“深度克隆”或“浅层克隆”,但此类术语主要源于对各种对象引用应该封装的内容缺乏明确性。如果对象Fred有一个封装可变状态的类类型字段,该状态Fred拥有(但不封装身份),则Fred该对象的副本应包含对该对象副本的引用。如果字段封装了不可变状态,则 的副本Fred可以包含对原始对象或其任何不可变副本的引用。如果它封装了身份,则 的副本Fred 必须包含对原始对象的引用——而不是副本。如果它同时封装了身份和可变状态,那么Fred如果不复制它所属的整个互连对象林,则无法复制。

于 2013-06-04T15:31:30.123 回答
0

欢迎来到 Java bean 的可变世界。

你不能做你想做的事......但这是一个使用我写的几个接口的解决方案:

// Both classes in the same package

@Immutable // by contract
class Some implements Frozen<SomeBuilder>
{
    // All fields are final, package local
    final String name;

    // getters only -- NO setters

    public Some(final SomeBuilder builder)
    {
        name = builder.name;
        // other
    }

    // Return a thawed version
    @Override
    public SomeBuilder thaw()
    {
        return new SomeBuilder(this);
    }
}

@NotThreadSafe // by contract
class SomeBuilder implements Thawed<Some>
{
    // Mutable fields here, package local
    String name;
    // other

    // To create a new builder
    public SomeBuilder()
    {
    }

    // Package local constructor
    SomeBuilder(final Some some)
    {
        name = some.name;
        // etc
    }

    // Mutations
    public SomeBuilder setName(final String name)
    {
        this.name = name;
        return this;
    }

    // Return a frozen version
    @Override
    public Some freeze()
    {
        return new Some(this);
    }
}

现在,对于你的修改函数,让它返回一个新的数组。并使用.freeze()/从现有.thaw()实例中创建新实例。Some

于 2013-06-04T15:09:35.747 回答