2013-06-27 2 views
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 изменится только в том, что цикл - как только он закончил getSomeList() вернет хороший старый список, так что никаких изменений не будут сохранены. Также пытается вызвать getSomeList(). Add ("someString") приведет к исключению: UnsupportedOperationException. Пожалуйста, объясните (для ветерана C coder) логику этого поведения и как я поймаю это исключение?

+0

Вы не показываете достаточно «SomeClass». Я не вижу причины для 'UnsupportedOperationException' при попытке вызвать' add() '. – jlordo

+0

@jlordo: Я добавил недостающую строку кода (я думаю) –

+0

Это даже не компилируется ... – jlordo

ответ

2

Оператор 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"; 
    } 
} 
+0

что вы говорите, что java передает ссылки не указатели. Есть ли способ передать указатель в Java (это программист C во мне, говорящий ...). И +1 для текстовой графики. –

+0

просто '=' может передать ссылку в Java, разница в неизменности 'String', которая при необходимости создаст новый экземпляр. – keelar

+0

Для тех классов, которые созданы неизменными, у вас нет возможности изменить его после его создания. – keelar

1

Строки неизменяемые объекты, поэтому вы не можете их изменить.

Во-вторых, вы не можете выполнять код в теле класса. DoSomething класс должен иметь метод, который делает это:

public class DoSomething { 

    public static void doSomething() { 
     //code    
    } 
} 
2

Для списка, используйте Collections.unmodifiableList() возвращать список, который не является изменяемым извне (то есть, вы можете изменить список внутри вашего SomeClass).

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

Для строки, это безопасно вернуться, поскольку строка является immutable class.

+0

см. Мое редактирование. использовал это - забыли эту строку кода ... –

2

когда он закончил getSomeList() вернет хороший старый список, так что никаких изменений не сохранены.

Потому что, это Stringimmutable поэтому изменение не отражается в окончательном списке .. Используйте StringBuilder или StringBuffer вместо String в качестве элементов списка.

пытается также вызвать getSomeList() добавьте ("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").

+1

хорошая экспликация +1 – nachokk

0

От javadoc, Список stooges = Arrays.asList ("Larry", "Moe", "Curly"), он даст вам Returns список фиксированного размера, поддерживаемый указанным массивом. Таким образом, любая модификация этого списка приведет к ошибке java.lang.UnsupportedOperationException, даже если в коде не указано следующее: Collections.unmodifiableList (Arrayys.asList (someArray));