2

我有一门课SomeClass,里面有一个List<String> someList。我需要让其他类遍历列表元素,但不让它们更改列表或列表元素。测试以下代码:

public class SomeClass {
    static private List<String> someList;

    public static List<String> getSomeList() {
        return someList;
    }
    SomeClass(Array<String> someArray){
        someList=Collections.unmodifiableList(Arrays.asList(someArray));
    }

}
public class DoSomething {
    for (String s : SomeClass.getSomeList()){
    s+="Gotcha";
    }
}

将显示 s 只会在该 for 循环内更改 - 一旦完成 getSomeList() 将返回良好的旧列表,因此不会保存任何更改。同样尝试调用 getSomeList().add("someString") 将导致异常:UnsupportedOperationException。请(向资深的 C 编码人员)解释这种行为背后的逻辑以及如何捕获该异常?

4

5 回答 5

2

对于列表,使用Collections.unmodifiableList()返回一个在外部不可修改的列表(即,您仍然可以在您的内部修改列表SomeClass)。

public List<String> getInternalList() {
  return Collections.unmodifiableList(someList);
}

对于 String,返回是安全的,因为 String 是一个不可变的类

于 2013-06-27T18:46:00.300 回答
2

String的+=操作符实际上创建了一个 String 的新实例,而不是修改你原来的字符串对象。换句话说,下面两行代码是等价的:

s += "Gotcha"
s = new String(s + "Gotcha");

例子

下面是一个与您类似的示例doSomeThing

public class st {
  public static void main(String[] args) {
    String hello = "hello";
    String helloReference = hello; // making helloReference and hello refer to same object.
    helloReference += " world"; // perform +=
    System.out.format("`hello` = %s, `helloReference` = %s\n",
        hello, helloReference);
  }
}

其输出如下,表明 引用的对象hello不受+=执行的运算符的影响helloReference

hello = `hello`, helloReference = `hello world`

在其他世界中,在 += 运算符之前:

    hello            helloReference
      |                     |
   -------------------------------
   |        "hello"              |
   -------------------------------

在 += 运算符之后,它将创建一个新实例并修改helloReference引用的对象:

    hello            helloReference
      |                     |             --------------------
      |                     --------------|   "hello world"  |
   -------------------------------        --------------------
   |        "hello"              |
   -------------------------------

所以你的代码是安全的,在你 doSomeThing 中执行的操作将影响你的 SomeClass 引用的对象:

public class DoSomething {
  for (String s : SomeClass.getSomeList()){
    // this create an new string instance to s
    s+="Gotcha"; 
  }
}
于 2013-06-27T18:51:51.530 回答
2

一旦完成 getSomeList() 将返回良好的旧列表,因此不会保存任何更改。

因为,String不可变的,所以更改不会反映在最终列表中。使用StringBuilderorStringBuffer代替String作为列表的元素。

同样尝试调用 getSomeList().add("someString") 将导致异常:UnsupportedOperationException。

因为,Arrays.asList(someArray)返回的Arrays.ArrayList是在类中定义的嵌套类Arrays,如下所示:

private static class ArrayList<E> extends AbstractList<E>
         implements RandomAccess, java.io.Serializable

并且这个类正在扩展AbstractList,而后者又扩展了AbstractCollection。嵌套类ArrayList没有覆盖add(E e). AbstractList因此,当您调用add()它时,List它会调用其中定义如下的add(E e)方法:AbstractCollection

public boolean add(E e) {
             throw new UnsupportedOperationException();
        }

这就是为什么你UnsupportedOperationException在打电话时得到的原因add("something")

于 2013-06-27T18:59:36.967 回答
1

字符串是不可变的对象,所以你不能那样改变它们。

其次,您不能在类主体中执行代码。DoSomething类应该有一个方法来做到这一点:

public class DoSomething {

    public static void doSomething() {
         //code            
    }
}
于 2013-06-27T18:42:09.203 回答
0

从 javadoc , List stooges = Arrays.asList("Larry", "Moe", "Curly") ,它会给你一个返回一个由指定数组支持的固定大小的列表。因此,即使代码 Collections.unmodifiableList(Arrays.asList(someArray)); 中没有以下内容,对此列表的任何修改都将导致 java.lang.UnsupportedOperationException;

于 2013-06-27T19:00:31.973 回答