What is the proper encapsulation in java object types

What is the correct encapsulation from below 2 classes in java ?. I saw both of these elements in many codes (basically the 1st option). But it seems that the second approach is correct.

import java.util.Date; public class SomeClass { private Date date; public Date getDate() { return date; } public void setDate(Date date) { this.date = date; } } 

or

 import java.util.Date; public class SomeClass { private Date date; public Date getDate() { return (Date) date.clone(); } public void setDate(Date date) { this.date = (Date) date.clone(); } } 
+4
source share
8 answers

It depends on whether the type of the field you get / set is immutable or not, i.e. if they can be changed after their construction.

The point behind the entire Getter / Setter paradigm is that instance private / protected fields cannot be arbitrarily changed (or accessed) by any external class.

So, in your first example, the class can get a reference to the Date personal field, and then (since Date not immutable) use the Date setTime(long) method to change the date, effectively bypassing the Setter SomeClass method (and any side effects that may have, for example, checking, updating a GUI element, etc.).
In your second example, this cannot be done, since the outer class will only receive a clone of the actual date, so any changes made after this will not affect the SomeClass original date SomeClass .

Bottom line :
It all depends on the type of your personal / protected fields and what you are trying to achieve / prevent.


Points to keep in mind:

  • clone() does not always return a deep clone of an object (especially for complex objects whose fields refer to other modified objects, etc.). Therefore, it should be used with caution (and awareness of its internal work).

  • Primitive data types and strings are immutable, so there is no need for clone() ing when getting / setting fields of these types.

+5
source

Using clone as is not recommended -

  • Usually, the clone method of an object creates a new instance of the same class and copies all the fields into a new instance and returns it = shallow copy. Object class provides the clone method and provides support for shallow copy . It returns “Object Type”, and you need to explicitly point cast back to your original object.

  • When you perform a deep copy, be careful, as you may fall into cyclic dependencies .

  • The clone is not for instantiation and initialization . It cannot be taken as the creation of a new object. Because the constructor of cloned objects can never be called in the process.

4. Another drawback (and many others .. :)), the clone prevents the use of final fields.

5. In the Singleton template, if superclass implements the public clone() method so that your subclass does not use this clone() class to get a copy, rewrite it and throw a CloneNotSupportedException

So, Approach 1 is better than Approach 2

+4
source

Two main features of encapsulation:

  • Save instance variables (with access modifier, often private).
  • Make public access methods and force code to use these methods, not direct access to the instance variable

It is always useful to create a protective copy of a mutable object at any time when it is passed to the Constructors and set methods or from the get methods in the class. If this is not done, then the caller can simply break encapsulation by changing the state of the object, which is simultaneously displayed for both the class and the caller. Also, do not use the clone method to create a protected copy of a parameter (mutable object), the type of which is a subclass of unreliable parties, as this can lead to an intentional or unintentional change in the internal state of an instance variable.
Based on all these rules, none of your approaches is correct.

Therefore, the correct way to track encapsulation in your code is:

 import java.util.Date; public class SomeClass { private Date date; public Date getDate() { return new Date(date.getTime()); } public void setDate(Date date) { this.date = new Date(date.getTime()); } } 
+4
source

This is a matter of security / encapsulation preference, the most basic encapsulation is the first approach you posted, the second, however, is a more advanced way of encapsulating . This also protects objects passed to the class by cloning.

Consider the following:

 public class SomeData { private final Point value; public SomeData (final Point value) { this.value = value; } public Point getValue( ) { return value; } } 

now the above snippet looks unchanged (similar to your example). However, there is a gap in this. view the snippet below.

  public final static void main(final String[] args) { Point position = new Point(25, 3); SomeData data = new SomeData(position); position.x = 22; System.out.println(data.getValue( )); } 

since we only pass a reference to a position variable that we can still change. Cloning this will help protect the variable position:

if we change the declaration from this:

 public SomeData (final Point value) { this.value = value; } 

before (similar to cloning)

  public SomeBetterData(final Point value) { this.value = new Point(value); } 

when we call the main method again:

 Point position = new Point(25, 3); SomeData data = new SomeData(position); position.x = 22; 

the data object will remain unchanged no matter what we do with position . I hope you understand why there is cloning.

+3
source

First! The second one will create a new object with clone() for each call to getDate() , this can be confusing, depending on your application. (i.e. if you want to call the Date method using aSomeDate.getDate().aMethod() )

A small sample to understand my poor English;)

 public class Main { public static void main(String[] args) { SomeDate sd = new SomeDate(new Date(1991, 3, 3)); System.out.println(sd); sd.getDate().setYear(2012); System.out.println(sd); } } 
+2
source

The second attempt to force defensive copying. Consider the Period class, which stores two dates, and the first should be before the second:

 public class Period { private Date first, second; public Period(Date first, Date second) { if(first.compareTo(second) > 0) throw new IllegalArgumentException("first > second"); this.first = first; this.second = second; } public Date getFirst() { return first; } public Date getSecond() { return second; } } 

At first glance, this sounds unusual, but look:

 Date d1 = new Date(), d2 = new Date(); Period p = new Period(d1, d2) // no exception d1.setYear(98); // broken period precondition 

To solve this problem, a protective copy occurs, that is, the internal instance cannot be changed by the original parameter. Although your second approach will work, it is still hacked because a subclass can override clone() and save all newly created instances ...

The right way:

 this.first = new Date(first.getTime()); 

And the return statements:

 return new Date(first.getTime()); // here you may use clone.... 

Thus, for a subclass, there is no way to get a hand from internal elements.

+2
source

It depends on the code that SomeClass uses. If you plan to include this class in a library that is used by a third party, or your code interacts / runs code that you do not control, you absolutely need to encapsulate the latter form.

Even when you are not in such a difficult situation, it is worth protecting against a design error, which is a mutable Date class and returns a new Date instance. This is the reason why all the IDEs that I know designate this as a warning.

Although this usually leads to something like this for me:

 public class MyClass { private Date myDate; public Date getDate() { return myDate != null ? new Date(myDate.getTime()) : null; } public void setDate(Date date) { myDate = (date != null ? new Date(date.getTime()) : null); } } 

So IMHO, I would use the second option or switch to Jodas DateTime .

+1
source

Not for this specific example, but for the general approach:

It is always useful to provide the user with a way to pass the returned object to any other compatible class that he needs.

So, the first method looks good, because if the returned object was extended by another class, then it will be easy to convert to this type, instead of providing an object of a fixed type.

So in this way you provide a more general object, or I can say abstraction and encouraging polymorphism .

0
source

Source: https://habr.com/ru/post/1487215/


All Articles