Окончателни параметри на настройка в Java

Винаги съм програмирал на java и наскоро започнах да уча C++.

В C++ е конвенционално да се задават параметри на сетер като const, защо не виждаме това толкова често в java?

Имам предвид има ли някакви недостатъци при създаването на сетер като този:

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

vs

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

Първият трябва да наложи Object param o да остане постоянен през цялата set функция, а не?

Редактиране:

Краен параметър би наложил това да НЕ се случи:

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

Потребителят никога няма да може да зададе името различно от „Карлос“


person Koen Demonie    schedule 28.06.2015    source източник
comment
Тъй като в c++ можете да изпращате параметри като справка и указатели, което позволява на разработчика да промени първоначалната стойност, понякога по погрешка   -  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 трябва да може да определи дали дадена променлива/параметър е всъщност окончателен (за анонимни вътрешни класове), оптимизацията не е фактор AFAIK.

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

  1. Низът се предава чрез указател, достъпът се дереферира автоматично.
  2. Ако действителният аргумент е променлива, самата променлива не се променя.
  3. Имайте предвид, че може да има оператор за преобразуване за предаване на нещо различно от const string&.

Сега java:

  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 е Integer или друг числов обвиващ клас и дори така, превръщането му в окончателен няма да попречи на някой да създаде:

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"; 
}

Ето къде идва опасността и къде финалът не помага. Да кажем, че имате клас, наречен Име:

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, с поле за име и сетер. Това е опасен код:

class Foo {
   private Name name;

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

Тъй като не само променя състоянието на полето, то променя състоянието на препратката за име в извикващия код и модификаторът final няма да помогне ни най-малко. Решението: направете името неизменно.

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
Е, ако не е final, функцията за настройка може да промени стойността си и да загуби първоначалната зададена стойност...нищо прекалено голямо, но добавянето на final не вреди и може да реши този проблем - 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: Загрижен съм за получаването на фалшиво чувство за сигурност, когато правя това. Проблемните бъгове са, когато кодът в един клас причинява невидими странични ефекти в друг и отново декларирането на параметър като окончателен изобщо няма да предпази от това. - 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, а го правим в c++. Все още страхотно благодаря! +1 - person Koen Demonie; 29.06.2015