Подтвердить что ты не робот

Зачем объявлять окончательный окончательный класс на Java?

Я читал, что для того, чтобы сделать класс неизменным на Java, мы должны сделать следующее:

  • Не предоставлять никаких сеттеров
  • Отметить все поля как частные
  • Сделать окончательный класс

Почему требуется шаг 3? Почему я должен отмечать класс final?

4b9b3361

Ответ 1

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

public class Immutable {
     private final int value;

     public Immutable(int value) {
         this.value = value;
     }

     public int getValue() {
         return value;
     }
}

Теперь предположим, что я делаю следующее:

public class Mutable extends Immutable {
     private int realValue;

     public Mutable(int value) {
         super(value);

         realValue = value;
     }

     public int getValue() {
         return realValue;
     }
     public void setValue(int newValue) {
         realValue = newValue;
     }

    public static void main(String[] arg){
        Mutable obj = new Mutable(4);
        Immutable immObj = (Immutable)obj;              
        System.out.println(immObj.getValue());
        obj.setValue(8);
        System.out.println(immObj.getValue());
    }
}

Обратите внимание, что в моем подклассе Mutable я переопределил поведение getValue, чтобы прочитать новое изменчивое поле, объявленное в моем подклассе. В результате ваш класс, который изначально выглядит неизменным, действительно не является неизменным. Я могу передать этот объект Mutable везде, где ожидается объект Immutable, который мог бы делать очень плохие вещи для кода, предполагая, что объект действительно неизменен. Маркировка базового класса final предотвращает это.

Надеюсь, это поможет!

Ответ 2

Вопреки тому, что многие считают, создание обязательного класса final не требуется.

Стандартный аргумент для создания неизменяемых классов final заключается в том, что если вы этого не сделаете, то подклассы могут добавлять изменчивость, тем самым нарушая контракт суперкласса. Клиенты класса возьмут на себя неизменность, но будут удивлены, когда что-то выйдет из-под них.

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

Соответствие контракту вашего суперкласса не является тем, что может или должно всегда выполняться компилятором. Компилятор может обеспечить выполнение определенных аспектов вашего контракта (например: минимальный набор методов и их типа), но существует множество частей типичных контрактов, которые не могут быть реализованы компилятором.

Неизменяемость является частью договора класса. Это немного отличается от некоторых вещей, к которым люди больше привыкли, потому что в нем говорится, что класс (и все подклассы) не могут сделать, хотя я думаю, что большинство программистов на Java (и вообще ООП) склонны думать о контрактах как относящихся к что класс может сделать, а не то, что он не может сделать.

Неизменяемость также влияет не только на один метод; это влияет на весь экземпляр; но это не сильно отличается от способа equals и hashCode в работе Java. Эти два метода имеют конкретный контракт, изложенный в Object. В этом контракте очень тщательно излагаются те вещи, которые эти методы не могут сделать. Этот контракт более определен в подклассах. Очень легко переопределить equals или hashCode таким образом, который нарушает контракт. Фактически, если вы переопределите только один из этих двух методов без другого, есть вероятность, что вы нарушите контракт. Значит, equals и hashCode были объявлены final в Object, чтобы избежать этого? Я думаю, что большинство из них утверждают, что они не должны. Точно так же нет необходимости создавать неизменяемые классы final.

Тем не менее, большинство ваших классов, неизменяемых или нет, вероятно, должно быть final. См. "Эффективное Java Second Edition", пункт 17: "Дизайн и документ для наследования или запрет".

Таким образом, правильная версия вашего шага 3 будет: "Сделать окончательный класс или, при разработке для подкласса, четко документировать, что все подклассы должны оставаться неизменными".

Ответ 3

Не отмечайте окончание всего класса.

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

Лучше пометить ваши свойства как частными, так и окончательными, и если вы хотите защитить "контракт", отметьте ваши получатели окончательными.

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

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

Ответ 4

Если это не окончательно, кто-то может расширить класс и делать все, что ему нравится, например, предоставлять сеттеры, скрывать ваши частные переменные и в основном сделать его изменчивым.

Ответ 5

Это ограничивает другие классы, расширяющие ваш класс.

окончательный класс не может быть расширен другими классами.

Если класс расширяет класс, который вы хотите сделать неизменным, он может изменить состояние класса из-за принципов наследования.

Просто уточните, "это может измениться". Подкласс может переопределять поведение суперкласса, как использование переопределения метода (например, templatetypedef/Ted Hop)

Ответ 6

Если вы не сделаете это окончательным, я могу расширить его и сделать его не изменяемым.

public class Immutable {
  privat final int val;
  public Immutable(int val) {
    this.val = val;
  }

  public int getVal() {
    return val;
  }
}

public class FakeImmutable extends Immutable {
  privat int val2;
  public FakeImmutable(int val) {
    super(val);
  }

  public int getVal() {
    return val2;
  }

  public void setVal(int val2) {
    this.val2 = val2;
  }
}

Теперь я могу передать FakeImmutable любому классу, который ожидает Immutable, и он не будет вести себя как ожидаемый контракт.

Ответ 7

Для создания неизменяемого класса необязательно отмечать класс как final.

Позвольте мне взять один из таких примеров из классов java сам класс "BigInteger" неизменен, но не является окончательным.

На самом деле неизменяемость - это концепция, согласно которой объект создан, а затем не может быть изменен.

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

Неизменяемость означает, что невозможно изменить состояние объекта после его создания, и это достигается тремя правилами большого пальца, которые заставляют компилятор распознавать этот класс неизменным, и они следующие: -

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

Для получения дополнительной информации обратитесь к приведенному ниже URL

http://javaunturnedtopics.blogspot.in/2016/07/can-we-create-immutable-class-without.html

Ответ 8

Предположим, что следующий класс не был final:

public class Foo {
    private int mThing;
    public Foo(int thing) {
        mThing = thing;
    }
    public int doSomething() { /* doesn't change mThing */ }
}

Он, по-видимому, неизменен, потому что даже подклассы не могут изменять mThing. Однако подкласс может быть изменен:

public class Bar extends Foo {
    private int mValue;
    public Bar(int thing, int value) {
        super(thing);
        mValue = value;
    }
    public int getValue() { return mValue; }
    public void setValue(int value) { mValue = value; }
}

Теперь объект, который присваивается переменной типа Foo, более не гарантируется. Это может вызвать проблемы с такими вещами, как хэширование, равенство, concurrency и т.д.

Ответ 9

Дизайн сам по себе не имеет значения. Дизайн всегда используется для достижения цели. Какова цель здесь? Хотим ли мы уменьшить количество сюрпризов в коде? Мы хотим предотвратить ошибки? Мы слепо следуем правилам?

Кроме того, дизайн всегда стоит дорого. Каждый дизайн, который заслуживает имени, означает, что у вас есть конфликт целей.

С учетом этого вам нужно найти ответы на следующие вопросы:

  • Сколько очевидных ошибок это предотвратит?
  • Сколько тонких ошибок это предотвратит?
  • Как часто это сделает другой код более сложным (= больше подвержено ошибкам)?
  • Это упрощает или упрощает тестирование?
  • Насколько хороши разработчики в вашем проекте? Сколько пособий с кувалдой им нужно?

Скажем, у вас в вашей команде много младших разработчиков. Они будут отчаянно пробовать любую глупую вещь только потому, что они не знают хороших решений для своих проблем. Создание финала класса может предотвратить ошибки (хорошие), но также может заставить их придумать "умные" решения, такие как копирование всех этих классов в изменчивые в коде.

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

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

Заключение: для этого ответа нет "лучшего" решения. Это зависит от того, какую цену вы желаете и которую вы должны заплатить.