Финальные параметры установки в Java

Я всегда программировал на Java, а недавно начал изучать C++.

В C++ принято устанавливать параметры установки как константы, почему мы не видим этого в Java?

Я имею в виду, есть ли недостатки в создании такого сеттера:

public void setObject(final Object o){ this.o=o; }

vs

public void setObject(Object o){ this.o=o; }

Первый должен обеспечивать, чтобы параметр объекта o оставался постоянным на протяжении всей заданной функции, а не ?

Редактировать:

Последний параметр заставит это НЕ произойти:

public void setName(String name){ 
     name="Carlos";
     this.name=name; 
}

Пользователь никогда не сможет установить имя, отличное от «Карлос».


person Koen Demonie    schedule 28.06.2015    source источник
comment
Потому что в С++ вы можете отправлять параметры как ссылки и указатели, что позволяет разработчику изменить исходное значение, иногда по ошибке.   -  person    schedule 28.06.2015
comment
По поводу: stackoverflow.com/questions/4162531/   -  person    schedule 28.06.2015
comment
Поскольку Java всегда передается по значению, изменение ссылки внутри вызываемого метода по существу не является операцией. В то время как изменение состояния через ссылку будет вести себя как в C++.   -  person Elliott Frisch    schedule 28.06.2015
comment
Вы должны удалить тег C++, так как речь идет о Java; и это другой язык, чем C++.   -  person Thomas Matthews    schedule 28.06.2015
comment
final в Java не означает то же самое, что const в C++.   -  person Jesper    schedule 28.06.2015
comment
Не так ли? Пожалуйста, просветите...   -  person Koen Demonie    schedule 28.06.2015


Ответы (2)


Хорошо, final параметру/переменной нельзя назначить. Поскольку компилятор Java должен иметь возможность определять, является ли переменная/параметр на самом деле окончательным (для анонимных внутренних классов), оптимизация, насколько мне известно, не имеет значения.

Дело в том, что C++ имеет больший набор инструментов, который Java попыталась уменьшить. Следовательно, важно использовать С++ const string&, говоря

  1. Строка передается по указателю, доступ автоматически разыменовывается.
  2. Если фактический аргумент является переменной, сама переменная не изменяется.
  3. Имейте в виду, что может быть оператор преобразования для передачи чего-то другого, кроме const string&.

Теперь джава:

  1. Java не размещает объекты в стеке, а хранит в стеке только примитивные типы и дескрипторы объектов.
  2. В Java нет выходных параметров: переменная, переданная в вызов метода, никогда не изменит своего непосредственного значения.

Вернемся к вашему вопросу:

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

Тем не мение:

public final void setXyz(Xz xyz) {
    this.xyz = xyz;
}

более полезен: этот метод нельзя переопределить, и, следовательно, его можно безопасно использовать в конструкторе. (Вызов переопределенного метода в конструкторе будет в контексте еще не инициализированного дочернего экземпляра.)

person Joop Eggen    schedule 28.06.2015

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


Изменить
Вы опубликовали:

public void setObject(Object o){ 
     o++; // this does not compile
     this.o=o; 
}

Который смешивает примитивный числовой и ссылочный тип. Это имеет смысл только в том случае, если o является целым или другим числовым классом-оболочкой, и даже в этом случае создание final не помешает кому-то создать:

private void setI(final Integer i) {
   this.i = 1 + i;
}

Но ни ваш код, ни этот код выше не повлияют на объект параметра на стороне вызывающего кода.


Изменить
Итак, вы опубликовали:

public void setName(String name){ 
     name="Carlos";
     this.name=name; 
}

Но тогда кто-то мог написать

public void setName(final String name){ 
     this.name= name + " Carlos"; 
}

Вот где возникает опасность и где финал не помогает. Скажем, у вас есть класс с именем Name:

public class Name {
   private String lastName;
   private String firstName;
   public Name(String lastName, String firstName) {
      this.lastName = lastName;
      this.firstName = firstName;
   }
   public String getLastName() {
      return lastName;
   }
   public void setLastName(String lastName) {
      this.lastName = lastName;
   }
   public String getFirstName() {
      return firstName;
   }
   public void setFirstName(String firstName) {
      this.firstName = firstName;
   }
}

А затем класс Foo с полем Name и сеттером. Это опасный код:

class Foo {
   private Name name;

   public void setName(final Name name) {
      name.setFirstName("Carlos");
      this.name = name;
   }
}

Поскольку он меняет не только состояние поля, но и состояние ссылки Name в вызывающем коде, а модификатор final ни на йоту не поможет. Решение: сделать Name неизменяемым.

e.g.,

import java.util.Date;

// class should be declared final
public final class BetterName {
   private String lastName;
   private String firstName;
   private Date dateOfBirth;

   public BetterName(String lastName, String firstName, Date dob) {
      this.lastName = lastName;
      this.firstName = firstName;

      // make and store a private copy of non-immutable reference types
      dateOfBirth = new Date(dob.getTime()); 
   }

   // only getters -- no setters
   public String getLastName() {
      return lastName;
   }

   public String getFirstName() {
      return firstName;
   }

   public Date getDateOfBirth() {
      // return copies of non-immutable fields
      return new Date(dateOfBirth.getTime());
   }
}
person Hovercraft Full Of Eels    schedule 28.06.2015
comment
Да, но это будет означать, что было бы разумно установить ваши параметры как окончательные в ваших сеттерах (чтобы вы не потеряли начальное заданное значение)? тогда почему мы не рассматриваем это как условность? - person Koen Demonie; 28.06.2015
comment
@KoenDemonie: я не понимаю твое заявление выше. Практически нет преимуществ в том, чтобы сделать параметр установки окончательным. Что вы имеете в виду под "so you don't lose the initial given value"? Опять же, самое важное, что нужно сделать дома, — попытаться сделать ваши классы неизменяемыми, когда и где это возможно. - person Hovercraft Full Of Eels; 28.06.2015
comment
Что ж, если это не окончательное значение, функция установки может изменить свое значение и потерять начальное заданное значение... ничего особенного, но добавление окончательного значения не повредит и может решить эту проблему. - person Koen Demonie; 28.06.2015
comment
установка параметра в final приведет к принудительному выполнению вашего утверждения: установщик устанавливает, вот и все. ну это я так думаю... - person Koen Demonie; 28.06.2015
comment
Вы правы в том, что переназначение аргумента не повлияет на переданную переменную, поэтому будет хорошим напоминанием, если компилятор скажет вам, что это не сработает (как и ожидалось). И да, это преимущество относительно невелико, особенно для продвинутых разработчиков. Я лично предпочитаю помечать каждую переменную как final и предпочитать неизменяемый класс. Так что создание сеттеров с аргументом final — это просто часть привычки :D. Этот ответ также интересен в этом вопросе: Зачем объявлять конечные переменные внутри методов? - person Tom; 28.06.2015
comment
@Tom: меня беспокоит ложное чувство безопасности при этом. Ошибки проблемы возникают, когда код в одном классе вызывает невидимые побочные эффекты в другом, и опять же, объявление параметра как final не защитит от этого вообще. - person Hovercraft Full Of Eels; 28.06.2015
comment
О вашем примере BetterName: поскольку вы не собираетесь добавлять методы установки, вы можете показать это еще лучше, если каждое поле будет final само по себе. Таким образом, другие разработчики будут знать, что эта модель должна быть неизменной. - person Tom; 28.06.2015
comment
@HovercraftFullOfEels Это правильно, и именно по этой причине следует предпочесть оба: final переменные и неизменяемые типы (а не только final). И да, есть еще незащищенные случаи/процессы, о которых нужно знать, но это не аргумент, чтобы защитить себя от нежелательных переназначений. Но да (опять же :D) программист должен помнить, что final имеет свои ограничения. - person Tom; 28.06.2015
comment
Не может ли комментарий «класс должен быть объявлен окончательным» создать впечатление, что окончательные классы неизменяемы для неопытных читателей? - person Bastien Léonard; 28.06.2015
comment
спасибо, к сожалению, я не могу принять два ответа. Другой ответ помог мне немного больше понять, почему мы не делаем этого в Java, а делаем в С++. Все равно большое спасибо! +1 - person Koen Demonie; 29.06.2015