Можно ли сделать объект "Только для чтения" методу - программирование
Подтвердить что ты не робот

Можно ли сделать объект "Только для чтения" методу

Если ссылка объекта передается методу, возможно ли сделать объект "Только для чтения" методу?

4b9b3361

Ответ 1

Не строго говоря. То есть ссылка, которая может мутировать объект, не может быть превращена в ссылку, которая не может мутировать объект. Кроме того, нет способа выразить, что тип является неизменным или изменяемым, кроме использования условных обозначений.

Единственная функция, обеспечивающая некоторую форму неизменяемости, будет final поля - после их написания они не могут быть изменены.

Тем не менее, существуют способы разработки классов, чтобы предотвратить нежелательную мутацию. Вот несколько методов:

  • Оборонительное копирование. Передайте копию объекта, так что, если он мутирован, он не нарушит ваши внутренние инварианты.

  • Используйте модификаторы доступа и/или интерфейс, чтобы выставлять только методы только для чтения. Вы можете использовать модификаторы доступа (public/private/protected), возможно, в сочетании с интерфейсом, так что только некоторые методы видны другому объекту. Если методы, которые подвергаются действию, доступны только для чтения, вы в безопасности.

  • Сделайте свой объект неизменным по умолчанию. Любая операция над объектом фактически возвращает копию объекта.

Также обратите внимание, что API в SDK иногда использует методы, которые возвращают неизменяемую версию объекта, например. Collections.unmodifiableList. Попытка изменить неизменяемый список вызовет исключение. Это не статично ставит статичность (во время компиляции с системой статического типа), но это дешевый и эффективный способ ее динамического (во время выполнения).

Было много предложений по исследованию расширения Java для лучшего контроля над псевдонимом и доступностью. Например, добавление ключевого слова readonly. Ни один из них не настолько, насколько я знаю, запланирован для включения в будущую версию Java. Вы можете взглянуть на эти указатели, если вам интересно:

Checker Framework очень интересен. В Framework Checker просмотрите контрольную панель Generic Universe Types, проверку целостности IGJ и проверку неизменяемости Javari. Структура работает с использованием аннотаций, поэтому она не навязчива.

Ответ 2

Нет, не без украшения, композиции, клонирования и т.д.

Ответ 3

Для этого нет общего механизма. Для этого вам нужно написать специальный код, например, написать неизменяемую оболочку (см. Collections.unmodifiableList).

Ответ 4

В большинстве случаев вы могли бы достичь аналогичной вещи, клонировав Object в качестве первого выражения метода, такого как...

public void readOnlyMethod(Object test){
    test = test.clone();
    // other code here
}

Итак, если вы вызвали readOnlyMethod() и перешли в любой Object, будет выполнен клоун Object. Клон использует то же имя, что и параметр метода, поэтому нет риска случайного изменения оригинала Object.

Ответ 5

Нет. Но вы можете попытаться клонировать объект перед его передачей, поэтому любые изменения, сделанные этим методом, не будут влиять на исходный объект.

Ответ 6

чтобы реализовать интерфейс, который имеет только методы только для чтения (без методов настройки), это дает копию объекта (только для дороги) и возвращает только экземпляр интерфейса только для чтения, а не возвращает экземпляр самого объекта

Ответ 7

Вы можете определить все параметры объектов как final, но это сделает объект доступным только для всех.

Ответ 8

В зависимости от того, где вы хотите, чтобы это правило соблюдалось. Если вы работаете совместно над проектом, используйте final с комментарием, говорящему следующему человеку, что они не предназначены для изменения этого значения. В противном случае вы не просто напишите метод, чтобы не касаться объекта?

public static void main(String[] args) {
    cantTouchThis("Cant touch this");
}

/**
 * 
 * @param value - break it down
 */
public static void cantTouchThis(final String value) {
    System.out.println("Value: " + value);
    value = "Nah nah nah nah"; //Compile time error
} 

Так специально для этого метода значение никогда не будет записано, и оно принудительно выполняется во время компиляции, делая решение чрезвычайно надежным. Вне сферы применения этого метода объект остается неизменным без необходимости создания какой-либо оболочки.

Ответ 9

private boolean isExecuteWriteQueue = false;
public boolean isWriting(){
    final boolean b = isExecuteWriteQueue;
    return b;
}

Ответ 10

Расширяя ответ на ewernli...

Если вы являетесь владельцем классов, вы можете использовать интерфейсы только для чтения, чтобы методы, использующие ссылку объекта только для чтения, могли получать только копии дочерних элементов, доступные только для чтения; в то время как основной класс возвращает доступные для записи версии.

пример

public interface ReadOnlyA {
    public ReadOnlyA getA();
}

public class A implements ReadOnlyA {
    @Override
    public A getA() {
        return this;
    }

    public static void main(String[] cheese) {
        ReadOnlyA test= new A();
        ReadOnlyA b1 = test.getA();
        A b2 = test.getA(); //compile error
    }
}

Если вы не являетесь владельцем классов, вы можете расширить класс, переопределив сеттеры, чтобы выдать ошибку или не выполнять операции, и использовать отдельные сеттеры. Это фактически сделает ссылку на базовый класс доступной только для чтения, однако это может легко привести к путанице и трудным для понимания ошибкам, поэтому убедитесь, что они хорошо документированы.

Ответ 11

Я полагаю, что ваш реальный вопрос заключается в том, чтобы избежать побег ссылки.

Как указано в некоторых ответах, чтобы извлечь интерфейс из класса и выставить только методы get. Это предотвратит случайную модификацию, но опять же не является надежным решением, чтобы избежать вышеуказанной проблемы.

Рассмотрим пример ниже:

Customer.java:

public class Customer implements CustomerReadOnly {

    private String name;
    private ArrayList<String> list;

    public Customer(String name) {
        this.name=name;
        this.list = new ArrayList<>();
        this.list.add("First");
        this.list.add("Second");
    }

    @Override
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    @Override
    public ArrayList<String> getList() {
        return list;
    }
    public void setList(ArrayList<String> list) {
        this.list = list;
    }

}

CustomerReadOnly.java:

public interface CustomerReadOnly {

    String getName();

    ArrayList<String> getList();

}

Main.java:

public class Test {
    public static void main(String[] args) {
        CustomerReadOnly c1 = new Customer("John");

        System.out.println("printing list of class before modification");
        for(String s : c1.getList()) {
            System.out.println(s);
        }

        ArrayList<String> list = c1.getList();
        list.set(0, "Not first");

        System.out.println("printing list created here");
        for(String s : list) {
            System.out.println(s);
        }

        System.out.println("printing list of class after modification");
        for(String s : c1.getList()) {
            System.out.println(s);
        }
    }
}

Ouput:

printing list of class before modification
First
Second
printing list created here
Not first
Second
printing list of class after modification
Not first
Second

Итак, как вы можете видеть, извлечение интерфейса и предоставление только методов get работает, только если у вас нет изменяемой переменной-члена.

Если у вас есть коллекция в качестве переменной-члена, ссылку на которую вы не хотите получить от класса, вы можете использовать Collections.unmodifiableList() как указано в ответе ewernli.

При этом никакой внешний код не может изменить базовую коллекцию, и ваши данные полностью доступны только для чтения.

Но опять же, когда речь заходит о пользовательских объектах, выполняющих то же самое, я знаю только метод Interface, который может предотвратить изменение случайно, но не уверен в надежном способе избежать выхода из ссылки.