2012-02-21 116 views
2

我正在查看一些代码,并且我想确保我了解是否对对象所做的某些更改实际上是针对这些对象或这些对象的副本进行的。我是否在更改该对象的对象或副本?

这里有一个例子:

for(int i = 0; i < myList.size(); i++) 
{ 
    DataObject myItem = (DataObject)myList.get(i); 
    myItem.setString("someKey", "someValue"); 
} 

myList中的数据对象的名单,所以我不知道目的是调用列表的get()方法后铸造项目的数据对象的内容。但是我想知道它在编译时是如何处理的 - 这个强制转换是否会创建一个新对象,然后setString()方法将在该新对象上被调用并且不会影响列表中的对象?或者是myItem引用列表中的实际项目?

谢谢。

回答

2

它在铸造后仍然是对列表中项目的引用。没有创建副本。

List返回Object类的一个类型,以便它可以用于任何类型的Object(因为所有类都扩展为Object)。不过为了调用setString,你需要将引用类型转换为更具体的东西,以便编译器知道你要调用它的是什么类型的对象。

使用泛型例如List与您的列表一起指定类型将意味着您不必投射,但如果您无法控制列表创建,或者不知道或想要限制其中可能包含的对象类型,则可能不适合。

0

如果使用泛型,则不需要强制转换,否则需要强制转换,因为无法知道对象列表的类型。

但我不知道如何当它被编译,这是处理 - 将投创建新的对象

编译器不会创建任何对象。它只是检查语法和语义。编译该特定行时,编译器知道引用是DataObject类型,但list.get()返回导致不兼容类型的对象类型。所以,你需要明确施放。

1

演员不会创建一个新的对象,它只会告诉编译器'嘿,我知道我在做什么,这是一个DataObject'。

不知道实际的List实现的类型,我们不能说你得到的是不是同一个对象。如果您正在使用Java集合集合(ArrayList,LinkedList等)之一,那么它将是同一个对象实例。但我可以想象一个实现(当然是我自己制作的),它可以返回副本而不是底层实例。

public class MyList<T> implements List<T> { 
    private List<T> backingList = new ArrayList<T>(); 

    //methods to fulfil List interface contract... 

    @Override 
    public T get(int index) { 
     T t = backingList.get(index); 
     return makeCopy(t); 
    } 

    private T makeCopy(T t) { 
     //make a copy of t! 
    } 
} 

而正是因为如此,我们不能告诉(没有你告诉我们的基本类型),如果get(int)方法将返回一个副本或没有。

+0

+1关于不知道列表将在内部做什么的非常好的观点。这是使用不可变列表元素的另一个原因。 – biziclop 2012-02-21 22:03:54

+0

噢,我们都假设这是一个行为良好的List实现。但get()方法*可能会返回副本。但这不太可能。但是演员本身不会创建新的对象。好决定。 – 2012-02-21 22:05:00

+0

好点!我应该提到,'myList'是在代码中通过调用'DataObject'类型的对象的getList()'方法来声明的。 – Poosh 2012-02-21 22:23:39

0

该转换不会创建新的数据对象。当将原语转换为包装类型时,演员通常不会创建新对象,,或者将转换为包装类型,或者将明确或隐含的转换转换为字符串,从而调用方法toString()

1

它引用在列表中的实际项目,所有演确实是告诉编译器忽略声明类型,你知道什么对象的实际类型将是。 (但是如果你错了,你会得到一个ClassCastException的运行时间,这就是为什么要尽可能避免施法的原因。没有详细说明,泛型可以帮助解决这个问题。)

现在,这种事情(修改集合的元素)适用于没有问题的列表,但是Set s和Map的键您必须使用不可变对象(基本上,在调用构造函数后其字段不能更改的对象),否则可以右对齐一塌糊涂。如果你的集合在多个线程之间共享,你也应该避免这种情况。

+0

+1回到你身边。 – 2012-02-21 22:05:58

0

myList.get()可能被定义为返回一个更通用的类型,例如Object。你不能使一个引用变量引用一个比它自己更通用的类型。因此,您必须进行演员制作 -

DataObject myItem = (DataObject) myList.get(i); 

演员阵容不会创建新对象。转换只会使变量myItem引用列表中已有的对象。因此,setString()方法会更改您的实际对象。因此,列表中的对象也会发生更改,因为它是同一个对象。

0

如果myList被声明为一个类型列表,像这样:

List<DataObject> myList = new ArrayList<DataObject>(); 

然后铸造是没有必要的。但是,如果它是无类型List,则其get方法将返回类型为Object的对象。这可能是为什么演员是必要的(没有看到声明,我不知道)。

话虽如此,演员并没有创建一个新的对象,所以你得到的是一个新的对列表中已经存在的对象的引用。这意味着任何修改都发生在myList之内。

+0

通过调用代码中较早的'DataObject'上的'getList()'方法来声明变量'myList'。我可能应该提到这一点。但是,这是有道理的,他们必须这样写,因为get()方法正在返回Object类型的对象。谢谢! – Poosh 2012-02-21 22:20:10

0

它可能是相同的对象。

唯一创建new对象的方法是什么? new关键字。有些人认为clone()创建了一个新对象(如果正确实施),但是该怎么办?在克隆功能中有一个new关键字!

现在,我不知道你在处理什么样的myList,但假设它是标准Java库(ArrayList, LinkedList, etc)的一部分,那么你会得到同样的对象。

P.S.记住阵列也是对象!

相关问题