Какой смысл методов set и методов считывания в Java? [дубликат]

Немного связанный у Вас могут быть дополнительные аргументы. Значение Вас может назвать метод с любым количеством аргументов, если Вам нравится. Но это - функция от C (varargs).

пример, сообщение NSArray:

+ (id)arrayWithObjects:(id)firstObj, ...

Пример использования:

NSArray *myArray;
NSDate *aDate = [NSDate distantFuture];
NSValue *aValue = [NSNumber numberWithInt:5];
NSString *aString = @"a string";

myArray = [NSArray arrayWithObjects:aDate, aValue, aString, nil];

Это является прямым из документов Apple. Вы указываете на конец всех своих споров с ноль .

30
задан Josh Lee 12 November 2010 в 02:48
поделиться

11 ответов

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

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

27
ответ дан 27 November 2019 в 23:20
поделиться

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

Без геттеров и сеттеров вам пришлось бы изменять каждый код везде, где используется Milage, с геттерами / сеттерами вам в значительной степени (по крайней мере в идеальном мире) просто нужно изменить создание типа Milage.

0
ответ дан 27 November 2019 в 23:20
поделиться

Your example is extreme to the point of absurdity. Yes, all those getters and setters bloat the code and add no value in that case. But the underlying idea of encapsulation is meant for larger systems composed of many interacting components, not for small, self-contained programs.

Characteristics of useful, sensible uses of getters and setters:

  • A class that is used by many other classes (hiding implementation details makes it easier for the clients)
  • Getters and setters only for fields for which they're actually needed - as few as possible, most fields should be private and used only within their class
  • Very few setters in general: mutable fields make it much harder to keep track of the program's state than read-only fields
  • Getters and setters that actually do something besides accessing a fied, e.g. setters that throw exceptions for invalid values or update a "last modified" timestamp, or a getter that computes a value on the fly rather than relying on an underlying field
1
ответ дан 27 November 2019 в 23:20
поделиться

Ответ одним словом: интерфейсы .

Интерфейсы допускают методы, а не полей, поэтому установленное соглашение должно иметь для этой цели методы getX и setX.

(А интерфейсы - это способ отделения функциональности от реализации в Java)

1
ответ дан 27 November 2019 в 23:20
поделиться

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

Методы доступа

Скрытие / инкапсуляция информации

1
ответ дан 27 November 2019 в 23:20
поделиться

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

Mileage m = new Mileage();
m.miles = 5.0;
m.gallons = 10.0;
...

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

2
ответ дан 27 November 2019 в 23:20
поделиться

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

Взгляните на Статья в Википедии по этой теме.

3
ответ дан 27 November 2019 в 23:20
поделиться

Идея состоит в том, что если ваши клиентские классы вызывают функции get / set, вы можете изменить то, что они делают позже, и вызывающие будут изолированы. Если у вас есть общедоступная переменная, и я обращаюсь к ней напрямую, у вас нет возможности добавить поведение позже, когда к ней обращаются или устанавливаются.

Даже в вашем простом примере вы могли бы использовать больше ее преимуществ.

Вместо того, чтобы использовать:

milesPerGallon = miles / gallons;

в calculateM900 ()

Вы можете изменить setMiles () и setGallons (), чтобы обновлять milesPerGallon при их вызове. Затем удалите setMilesPerGallon (), чтобы указать, что это свойство доступно только для чтения.

5
ответ дан 27 November 2019 в 23:20
поделиться

Encapsulation

Accessor methods ("setters and getters") attempt to hide the details about how the data in an object is stored. In practice, they are a glorified means to store and retrieve data in a non-object-oriented fashion. Accessors do not effectively encapsulate anything in that there is little practical difference between the following two pieces of code:

Person bob = new Person();
Colour hair = bob.getHairColour();
hair.setRed( 255 );

And this:

Person bob = new Person();
Colour hair = bob.hairColour;
hair.red = 255;

Both code snippets expose the idea that a Person is tightly coupled to Hair. This tight coupling then reveals itself throughout the code base, resulting in brittle software. That is, it becomes difficult to change how a Person's hair is stored.

Instead:

Person bob = new Person();
bob.setHairColour( Colour.RED );

This follows the premise of "tell, don't ask." In other words, objects should be instructed (by other objects) to perform a specific task. This is the whole point of object-oriented programming. And very few people seem to get it.

The difference between the two scenarios is this:

  • In the first situation, Bob had no control over what colour his hair would become. Great for a hair stylist with a penchant for redheads, not so great for Bob who despises that colour.
  • In the second situation, Bob has complete control over what colour his hair will become because no other object in the system is allowed to change that colour without Bob's permission.

Another way to avoid this problem is to return a copy of Bob's hair colour (as a new instance), which is no longer coupled to Bob. I find that to be an inelegant solution because it means there is behaviour that another class desires, using a Person's hair, that is no longer associated with the Person itself. That reduces the ability to reuse code, which leads to duplicated code.

Hiding Data Types

In Java, which cannot have two method signatures that differ only by return type, it really does not hide the underlying data type used by the object. You will seldom, if ever, see the following:

public class Person {
  private long hColour = 1024;

  public Colour getHairColour() {
    return new Colour( hColour & 255, hColour << 8 & 255, hColour << 16 & 255 );
  }
}

Typically, the individual variables have their data type exposed verbatim by use of the corresponding accessor, and requires refactoring to change it:

public class Person {
  private long hColour = 1024;

  public long getHairColour() {
    return hColour;
  }

  /** Cannot exist in Java: compile error. */
  public Colour getHairColour() {
    return new Colour( hColour & 255, hColour << 8 & 255, hColour<< 16 & 255 );
  }
}

While it provides a level of abstraction, it is a thin veil that does nothing for loose coupling.

Tell, Don't Ask

For more information on this approach, read Tell, Don't Ask.

File Example

Consider the following code, slightly modified from ColinD's answer:

public class File {
   private String type = "";

   public String getType() {
      return this.type;
   }

   public void setType( String type ) {
      if( type = null ) {
        type = "";
      }

      this.type = type;
   }

   public boolean isValidType( String type ) {
      return getType().equalsIgnoreCase( type );
   }
}

The method getType() in this instance is redundant and will inevitably (in practice) lead to duplicated code such as:

public void arbitraryMethod( File file ) {
  if( file.getType() == "JPEG" ) {
    // Code.
  }
}

public void anotherArbitraryMethod( File file ) {
  if( file.getType() == "WP" ) {
    // Code.
  }
}

Issues:

  • Data Type. The type attribute cannot readily change from a String to an integer (or another class).
  • Implied Protocol. It is time consuming to abstract the type from the specific (PNG, JPEG, TIFF, EPS) to the general (IMAGE, DOCUMENT, SPREADSHEET).
  • Introduces Bugs. Changing the implied protocol will not generate a compiler error, which can lead to bugs.

Avoid the problem altogether by preventing other classes from asking for data:

public void arbitraryMethod( File file ) {
  if( file.isValidType( "JPEG" ) ) {
    // Code.
  }
}

This implies changing the get accessor method to private:

public class File {
   public final static String TYPE_IMAGE = "IMAGE";

   private String type = "";

   private String getType() {
      return this.type;
   }

   public void setType( String type ) {
      if( type == null ) {
        type = "";
      }
      else if(
        type.equalsIgnoreCase( "JPEG" ) ||
        type.equalsIgnoreCase( "JPG" ) ||
        type.equalsIgnoreCase( "PNG" ) ) {
        type = File.TYPE_IMAGE;
      }

      this.type = type;
   }

   public boolean isValidType( String type ) {
      // Coerce the given type to a generic type.
      //
      File f = new File( this );
      f.setType( type );

      // Check if the generic type is valid.
      //
      return isValidGenericType( f.getType() );
   }
}

No other code in the system will break when the File class transitions the implied protocol from specific types (e.g., JPEG) to generic types (e.g., IMAGE). All the code in the system must use the isValidType method, which does not give the type to the calling object, but tells the File class to validate a type.

17
ответ дан 27 November 2019 в 23:20
поделиться

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

Возьмем, например, , файл (игнорируя существование класса File в Java). Этот класс File имеет поле для хранения типа файла (.pdf, .exe, .txt и т. Д.) ... все остальное мы проигнорируем.

Первоначально вы решаете сохранить его как String без геттеров и сеттеров:

public class File {
   // ...
   public String type;
   // ...
}

Вот некоторые проблемы с неиспользованием геттеров и сеттеров.

Нет контроля над тем, как устанавливается поле:

Любые клиенты вашего класса могут делают с ним то, что они хотят:

public void doSomething(File file) {
   // ...
   file.type = "this definitely isn't a normal file type";
   // ...
}

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

Невозможность легко изменить внутреннее представление:

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

Как геттеры и сеттеры решают эту проблему

Теперь представьте, что вместо этого вы сделали поле типа частным и создали

public String getType() {
   return this.type;
}

public void setType(String type) {
   this.type = type;
}

Контроль над настройкой свойство:

Теперь, если вы хотите реализовать требование, что только определенные строки являются допустимыми типами файлов и не допускают других строк, вы можете просто написать:

public void setType(String type) {
   if(!isValidType(type)) {
       throw new IllegalArgumentException("Invalid file type: " + type);
   }
   this.type = type;
}

private boolean isValidType(String type) {
   // logic here
}

Возможность легко изменить внутреннее представление:

Изменение String представления типа относительно легко. Представьте, что у вас есть перечисление ValidFileType , которое реализует FileType и содержит допустимые типы файлов.

Вы можете легко изменить внутреннее представление типа файла в классе, например это:

public class File {
   // ...
   private FileType type;
   // ...
   public String getType() {
      return type.toString();
   }

   public void setType(String type) {
      FileType newType = ValidFileType.valueOf(type);

      if(newType == null) {
         throw new IllegalArgumentException("Invalid file type: " + type);
      }

      this.type = newType;
   }
}

Поскольку клиенты класса в любом случае вызывали getType () и setType () , с их точки зрения ничего не меняется. Изменилось только внутреннее устройство класса, но не интерфейс, который используют другие классы.

Изменить представление типа String относительно просто. Представьте, что у вас есть перечисление ValidFileType , которое реализует FileType и содержит допустимые типы файлов.

Вы можете легко изменить внутреннее представление типа файла в классе, например это:

public class File {
   // ...
   private FileType type;
   // ...
   public String getType() {
      return type.toString();
   }

   public void setType(String type) {
      FileType newType = ValidFileType.valueOf(type);

      if(newType == null) {
         throw new IllegalArgumentException("Invalid file type: " + type);
      }

      this.type = newType;
   }
}

Поскольку клиенты класса в любом случае вызывали getType () и setType () , с их точки зрения ничего не меняется. Изменилось только внутреннее устройство класса, но не интерфейс, который используют другие классы.

Изменить представление типа String относительно просто. Представьте, что у вас есть перечисление ValidFileType , которое реализует FileType и содержит допустимые типы файлов.

Вы можете легко изменить внутреннее представление типа файла в классе, например это:

public class File {
   // ...
   private FileType type;
   // ...
   public String getType() {
      return type.toString();
   }

   public void setType(String type) {
      FileType newType = ValidFileType.valueOf(type);

      if(newType == null) {
         throw new IllegalArgumentException("Invalid file type: " + type);
      }

      this.type = newType;
   }
}

Поскольку клиенты класса в любом случае вызывали getType () и setType () , с их точки зрения ничего не меняется. Изменилось только внутреннее устройство класса, но не интерфейс, который используют другие классы.

Вы можете легко изменить внутреннее представление типа файла в классе следующим образом:

public class File {
   // ...
   private FileType type;
   // ...
   public String getType() {
      return type.toString();
   }

   public void setType(String type) {
      FileType newType = ValidFileType.valueOf(type);

      if(newType == null) {
         throw new IllegalArgumentException("Invalid file type: " + type);
      }

      this.type = newType;
   }
}

Поскольку клиенты класса вызывали getType () и setType () в любом случае, с их точки зрения, ничего не меняется. Изменилось только внутреннее устройство класса, но не интерфейс, который используют другие классы.

Вы можете легко изменить внутреннее представление типа файла в классе следующим образом:

public class File {
   // ...
   private FileType type;
   // ...
   public String getType() {
      return type.toString();
   }

   public void setType(String type) {
      FileType newType = ValidFileType.valueOf(type);

      if(newType == null) {
         throw new IllegalArgumentException("Invalid file type: " + type);
      }

      this.type = newType;
   }
}

Поскольку клиенты класса вызывали getType () и setType () в любом случае, с их точки зрения, ничего не меняется. Изменилось только внутреннее устройство класса, но не интерфейс, который используют другие классы.

17
ответ дан 27 November 2019 в 23:20
поделиться

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

class ExpensiveObject {
    private int foo;

    public ExpensiveObject() {
       // Does something that takes a long time.
    }

    public int getFoo() { return foo; }
    public void setFoo(int i) { foo = i; }
}

class ExpensiveObjectProxy extends ExpensiveObject {
    private ExpensiveObject realObject;

    public ExpensiveObjectProxy() { ; }

    protected void Load() {
       if ( realObject == null ) realObject = new ExpensiveObject();
    }

    public int getFoo() { Load(); return realObject.getFoo(); }
    public void setFoo(int i) { Load(); realObject.setFoo(i); }
}

class Main {
    public static void main( string[] args ) {
         // This takes no time, since ExpensiveOjbect is not constructed yet.
         ExpensiveObject myObj = new ExpensiveObjectProxy();

         // ExpensiveObject is actually constructed here, when you first use it.
         int i = myObj.getFoo();
    }
}

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

2
ответ дан 27 November 2019 в 23:20
поделиться
Другие вопросы по тегам:

Похожие вопросы: