Примеры неизменяемых классов - PullRequest
44 голосов
/ 26 февраля 2011

Я уже знаю определение неизменяемых классов, но мне нужно несколько примеров.

Ответы [ 10 ]

86 голосов
/ 26 февраля 2011

Некоторые известные неизменяемые классы в Стандартном API:

  • java.lang.String (уже упоминалось)
  • Классы-оболочки для примитивных типов: java.lang.Integer,java.lang.Byte, java.lang.Character, java.lang.Short, java.lang.Boolean, java.lang.Long, java.lang.Double, java.lang.Float
  • java.lang.StackTraceElement (используется при построении трассировки стека исключений)
  • Большинство перечисляемых классов являются неизменяемыми, но на самом деле это зависит от конкретного случая.(Не реализуйте изменяемые перечисления, это может вас испортить.) Я думаю, что по крайней мере все перечисляемые классы в стандартном API на самом деле неизменны.

  • java.math.BigIntegerи java.math.BigDecimal (по крайней мере, сами объекты этих классов, подклассы могут вводить изменчивость, хотя это не очень хорошая идея)

  • java.io.File.Обратите внимание, что это представляет собой внешний по отношению к ВМ объект (файл в локальной системе), который может существовать или не существовать, и имеет некоторые методы, изменяющие и запрашивающие состояние этого внешнего объекта.Но сам объект File остается неизменным.(Все остальные классы в java.io являются изменяемыми.)

  • java.awt.Font - представляет шрифт для рисования текста на экране (могут быть некоторые изменяемые подклассы, но это будетконечно, бесполезно)

  • java.awt.BasicStroke - вспомогательный объект для рисования линий в графическом контексте
  • java.awt.Color - (по крайней мере, объекты этого класса, некоторыеподклассы могут быть изменяемыми или зависеть от некоторых внешних факторов (например, системных цветов), а также от большинства других реализаций java.awt.Paint, таких как
    • java.awt.GradientPaint,
    • java.awt.LinearGradientPaint
    • java.awt.RadialGradientPaint,
    • (я не уверен насчет java.awt.TexturePaint)
  • java.awt.Cursor - представление растрового изображения для курсора мыши (здесь также некоторые подклассы могут быть изменяемыми или зависеть от внешних факторов)

  • java.util.Locale - представлять определенный географический, политический или культурный регион .

  • java.util.UUID - максимально глобальный уникальный идентификатор
  • , хотя большинство коллекций являются изменяемыми, в классе java.util.Collections есть некоторые методы-оболочки, которые возвращают неизменяемое представлениена коллекции.Если вы передадите им коллекцию, не известную нигде, это на самом деле неизменные коллекции.Кроме того, Collections.singletonMap(), .singletonList, .singleton возвращают неизменяемые одноэлементные коллекции, а также есть неизменяемые пустые.

  • java.net.URL и java.net.URI - представляет ресурс (в Интернете или где-то еще)

  • java.net.Inet4Address и java.net.Inet6Address, java.net.InetSocketAddress
  • большинство подклассов Java.security.Permission (представляющий разрешения, необходимые для какого-либо действия или предоставленные некоторому коду), но не java.security.PermissionCollection и подклассы.
  • Все классы java.time, за исключением DateTimeException, являются неизменяемыми.Большинство классов подпакетов java.time тоже неизменны.

Можно сказать, что примитивные типы тоже неизменны - вы не можете изменить значение 42, не так ли?


является классом AccessControlContext неизменным классом

AccessControlContext не имеет никаких методов мутации.И его состояние состоит из списка ProtectionDomains (который является неизменным классом) и DomainCombiner.DomainCombiner является интерфейсом, так что в принципе реализация может сделать что-то другое при каждом вызове

1081 * В самом деле, а также поведение ProtectionDomain может зависеть от текущей политики в силе. - спорно ли называть такиенеизменяемый объект.

и AccessController?

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

То же самое верно для всех других классов, которые не могут иметь объекты (экземпляры), наиболее известные:

  • java.lang.Void
  • java.lang.System (но у этого есть некоторое изменяемое статическое состояние - in, out, err)
  • java.lang.Math (это тоже - генератор случайных чисел)
  • java.lang.reflect.Array
  • java.util.Collections
  • java.util.Arrays
22 голосов
/ 26 февраля 2011

Неизменяемые классы не могут быть изменены после постройки.Так, например, Java String является неизменным.

Чтобы сделать класс неизменным, вы должны сделать его final и все поля private и final.Например, следующий класс является неизменяемым:

public final class Person {

     private final String name;
     private final int age;
     private final Collection<String> friends;

     public Person(String name, int age, Collection<String> friends) {
         this.name = name;
         this.age = age;
         this.friends = new ArrayList(friends);
     }

     public String getName() { 
         return this.name;
     }

     public int getAge() {
         return this.age;
     }

     public Collection<String> getFriends() {
         return Collections.unmodifiableCollection(this.friends);
     }
}

Я добавил метод в примере кода, показывающий, как обрабатывать коллекции, важный момент.

Где это возможно Вам следует делает классы неизменяемыми, потому что тогда вам не нужно беспокоиться о таких вещах, как безопасность потоков.

13 голосов
/ 26 февраля 2011

Важно помнить, что объявление класса как финального не означает, что он является «неизменяемым», это в основном означает, что этот класс не может быть расширен (или специализирован).и окончательные поля (без сеттеров), поэтому после его построения нельзя изменить значения полей.

9 голосов
/ 27 января 2014

Чтобы создать неизменяемый класс, необходимо выполнить следующие шаги:

  1. Объявить класс как окончательный, чтобы его нельзя было расширить.
  2. Сделать все поля закрытыми, чтобыпрямой доступ не разрешен.
  3. Не предоставляйте методы установки для переменных
  4. Сделайте все изменяемые поля окончательными, чтобы их значение могло быть присвоено только один раз.
  5. Инициализируйте всеполя через конструктор, выполняющий глубокое копирование.
  6. Выполните клонирование объектов в методах получения, чтобы вернуть копию, а не возвращать фактическую ссылку на объект.

Можно найти пример здесь .

Мы также можем использовать Pattern Builder для простого создания неизменяемых классов, пример можно найти здесь .

7 голосов
/ 19 февраля 2016

LocalDate , LocalTime и LocalDateTime Классы (начиная с 1.8) также являются неизменяемыми. На самом деле, именно этот вопрос находится на экзамене OCAJSE8 (1Z0-808), и именно поэтому я решил рассматривать его как не просто комментарий.

Все примитивные классы-обертки (такие как Boolean , Символ , Байт , Short , Integer , Long , Float и Double ) являются неизменяемыми.

Деньги и Валюта API (для Java9) также должен быть неизменным.

Кстати, Списки с поддержкой массива (созданные Arrays.asList(myArray)) являются структурно -иммутируемыми.

Кроме того, существуют некоторые пограничные случаи, такие как java.util.Optional (показанный на экзамене OCP, 1Z0-809), который является неизменным, если содержащийся элемент сам является неизменным.

3 голосов
/ 26 февраля 2011

В документации Sun (Oracle) есть отличный контрольный список того, как сделать неизменный объект.

  1. Не предоставляйте методы "setter" - методы, которые изменяют поля или объекты, на которые ссылаются поля.
  2. Сделайте все поля окончательными и приватными.
  3. Не разрешать подклассам переопределять методы. Самый простой способ сделать это - объявить класс как final. Более сложный подход - сделать конструктор частным и создать экземпляры в фабричных методах.
  4. Если поля экземпляра содержат ссылки на изменяемые объекты, не допускайте изменения этих объектов:
    • Не предоставляйте методы, которые изменяют изменяемые объекты.
    • Не делиться ссылками на изменяемые объекты. Никогда не храните ссылки на внешние изменяемые объекты, переданные в конструктор; при необходимости создайте копии и сохраните ссылки на копии. Аналогичным образом, при необходимости создайте копии ваших внутренних изменяемых объектов, чтобы избежать возврата оригиналов в ваши методы.

От: http://download.oracle.com/javase/tutorial/essential/concurrency/imstrat.html

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

3 голосов
/ 26 февраля 2011

String - хороший пример неизменного класса в реальном мире.И вы можете сравнить его с изменяемым классом StringBuilder.


Большинство классов Java, используемых для отражения, являются неизменяемыми.А некоторые другие «почти неизменны»: например, классы, которые реализуют Accessible, имеют только метод setAccessible, который изменяет состояние экземпляра Accessible.


Я уверенв стандартных библиотеках классов их намного больше.

2 голосов
/ 04 августа 2012

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

Изменяемые объекты - это объекты, состояние которых может быть изменено после создания. Пример - StringBuffer После изменения значения ячейка памяти изменяется.Смотрите пример ниже -

 public static void immutableOperation(){
    String str=new String("String is immutable class in Java object value cann't alter once created...");
    System.out.println(str);
    str.replaceAll("String", "StringBuffer");
    System.out.println(str);
    str.concat("Concating value ");
    System.out.println(str + "HashCode Value  " + str.hashCode());
    str=str.concat("Concating value ");
    System.out.println(str + "HashCode Val  " + str.hashCode());

}

public static void mutableOperation(){
    StringBuffer str=new StringBuffer("StringBuffer is mutable class in Java object value can  alter once created...");
    System.out.println(str + "HashCode Val - " + str.hashCode());
    str.replace(0, 12, "String");
    System.out.println(str + "HashCode Val - " + str.hashCode());

}
1 голос
/ 12 июля 2015

Мне нравится использовать примеры, которые имеют изменяемое свойство. Это помогает понять, как действительно функционируют неизменяемые классы.

Изменчивый класс

class MutableBook {
    private String title;

    public String getTitle(){
        return this.title;
    }

    public void setTitle(String title){
        this.title = title;
    }
}

И неизменяемая реализация, использующая изменяемый экземпляр книги.

public class ImmutableReader {
    private final MutableBook readersBook;
    private final int page;

    public ImmutableReader(MutableBook book) {
        this(book, 0);
    }

    private ImmutableReader(MutableBook book, int page){
        this.page = page;

        // Make copy to ensure this books state won't change.
        MutableBook bookCopy = new MutableBook();
        bookCopy.setTitle(book.getTitle());
        this.readersBook = bookCopy;
    }


    public MutableBook getBook() {
        // Do not return the book, but a new copy. Do not want the readers
        // book to change it's state if developer changes book after this call.
        MutableBook bookCopy = new MutableBook();
        bookCopy.setTitle(this.readersBook.getTitle());
        return bookCopy;
    }

    public int getPage() {
        // primitives are already immutable.
        return page;
    }

    /**
    * Must return reader instance since it's state has changed.
    **/ 
    public ImmutableReader turnPage() {
        return new ImmutableReader(this.readersBook, page + 1);
    }
}

Чтобы ваш класс был действительно неизменным, он должен соответствовать следующим циртериям:

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

Чтобы узнать немного больше, взгляните на мой пост в блоге: http://keaplogik.blogspot.com/2015/07/java-immutable-classes-simplified.html

0 голосов
/ 03 августа 2018

При создании объекта неизменяемого класса необходимо убедиться, что внешняя ссылка не будет сохранена. Однако значения здесь имеют значение. В приведенном ниже примере у меня есть класс с именем Fruits, внутри которого есть список. Я сделал класс неизменяемым, сделав список приватным и окончательным, а также не предоставив никакого сеттера.

  1. Если я создаю экземпляр объекта Fruit, конструктору будет предоставлен список. У программы-клиента уже есть ссылка на этот список (на стороне клиента), и, следовательно, список можно легко модифицировать и, следовательно, неизменность класс будет потерян.

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

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

  4. Это можно проверить, переопределив хеш-код () в неизменяемом классе. Независимо от того, сколько раз клиент изменяет список, хеш-код моего неизменяемого объекта класса останется неизменным, поскольку список, который он принимает, больше не указывает на внешний список.

публичный класс Fruit {

private final List<String> fruitnames;

public Fruit(List<String> fruitnames) {
    this.fruitnames = new ArrayList<>(fruitnames);
}

public List<String> getFruitnames() {
     return new ArrayList<>(fruitnames);
}

@Override
public int hashCode() {
    return getFruitnames() != null ? getFruitnames().hashCode(): 0;
}

}

// Клиентская программа

публичный класс ImmutableDemo {

public static void main(String args[]){

    List<String> fruitList = new ArrayList<>();
    fruitList.add("Apple");
    fruitList.add("Banana");
    //Immutable Object 1
    Fruit fruit1 = new Fruit(fruitList);
    //fruitHash is-689428840
    int fruitHash = fruit1.hashCode();

    System.out.println("fruitHash is" +fruitHash);
    //This value will not be added anymore as the state has already been defined and
    //now it cant change the state.
    fruitList.add("straberry");
    //fruitHash1 is-689428840
    int fruitHash1 = fruit1.hashCode();


    System.out.println("fruitHash1 is" +fruitHash1);
}

}

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...