Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Copy constructors and defensive copying

What is a copy constructor?

Can someone share a small example that can be helpful to understand along with defensive copying principle?

like image 601
user2094103 Avatar asked Feb 22 '13 09:02

user2094103


People also ask

What are defensive copies?

Note how the constructor Point(Point p) takes a Point and makes a copy of it - that's a copy constructor . This is a defensive copy because the original Point is protected from change by taking a copy of it.

What is difference between copy constructor and constructor?

Constructor: It is a method which has the same name as the class which is used to create an instance of the class. Copy Constructor: Used to create an object by copying variables from another object of the same class.

What is copy constructor?

Copy constructor is used to initialize the members of a newly created object by copying the members of an already existing object. Copy constructor takes a reference to an object of the same class as an argument.

What are copy constructors give an example?

Copy Constructor is called in the following scenarios: When we initialize the object with another existing object of the same class type. For example, Student s1 = s2, where Student is the class. When the object of the same class type is passed by value as an argument.


2 Answers

Here's a good example:

class Point {
  final int x;
  final int y;

  Point(int x, int y) {
    this.x = x;
    this.y = y;
  }

  Point(Point p) {
    this(p.x, p.y);
  }

}

Note how the constructor Point(Point p) takes a Point and makes a copy of it - that's a copy constructor.

This is a defensive copy because the original Point is protected from change by taking a copy of it.

So now:

// A simple point.
Point p1 = new Point(3,42);
// A new point at the same place as p1 but a completely different object.
Point p2 = new Point(p1);

Note that this is not necessarily the correct way of creating objects. It is, however, a good way of creating objects that ensures that you never have two references to the same object by accident. Clearly this is only a good thing if that is what you want to achieve.

like image 179
OldCurmudgeon Avatar answered Oct 09 '22 19:10

OldCurmudgeon


Copy constructors one often sees in C++ where they are needed for partly hidden, automatically invoked operations.

java java.awt.Point and Rectangle come to mind; also very old, mutable objects.

By using immutable objects, like String, or BigDecimal, simply assigning the object reference will do. In fact, due to the early phase of Java after C++, there still is a silly copy constructor in String:

public class Recipe {
    List<Ingredient> ingredients;

    public Recipe() {
        ingredients = new ArrayList<Ingredient>();
    }

    /** Copy constructor */
    public Recipe(Recipe other) {
        // Not sharing: ingredients = other.ingredients;
        ingredients = new ArrayList<>(other.ingredients);
    }

    public List<Ingredient> getIngredients() {
        // Defensive copy, so others cannot change this instance.
        return new ArrayList<Ingredient>(ingredients);
        // Often could do:
        // return Collections.immutableList(ingredients);
    }
}

On request

Leaking class with copy constructor:

public class Wrong {
    private final List<String> list;

    public Wrong(List<String> list) {
        this.list = list; // Error: now shares list object with caller.
    }

    /** Copy constructor */
    public Wrong(Wrong wrong) {
        this.list = wrong.list; // Error: now shares list object with caller.
    }

    public List<String> getList() {
        return list; // Error: now shares list object with caller.
    }

    public void clear() {
        list.clear();
    }
}

Correct class with copy constructor:

public class Right {
    private final List<String> list;

    public Right(List<String> list) {
        this.list = new ArrayList<>(list);
    }

    public Right(Right right) {
        this.list = new ArrayList<>(right.list);
    }

    public List<String> getList() {
        return new ArrayList<>(list);
    }

    public List<String> getListForReading() {
        return Collections.unmodifiableList(list);
    }

    public void clear() {
        list.clear();
    }
}

With testing code:

public static void main(String[] args) {
    List<String> list1 = new ArrayList<>();
    Collections.addAll(list1, "a", "b", "c", "d", "e");
    Wrong w1 = new Wrong(list1);
    list1.remove(0);
    System.out.printf("The first element of w1 is %s.%n", w1.getList().get(0)); // "b"
    Wrong w2 = new Wrong(w1);
    w2.clear();
    System.out.printf("Size of list1 %d, w1 %d, w2 %d.%n",
        list1.size(), w1.getList().size(), w2.getList().size());

    List<String> list2 = new ArrayList<>();
    Collections.addAll(list2, "a", "b", "c", "d", "e");
    Right r1 = new Right(list2);
    list2.remove(0);
    System.out.printf("The first element of r1 is %s.%n", r1.getList().get(0)); // "a"
    Right r2 = new Right(r1);
    r2.clear();
    System.out.printf("Size of list2 %d, r1 %d, r2 %d.%n",
        list2.size(), r1.getList().size(), r2.getList().size());
}

Which gives:

The first element of w1 is b.
Size of list1 0, w1 0, w2 0.
The first element of r1 is a.
Size of list2 4, r1 5, r2 0.
like image 21
Joop Eggen Avatar answered Oct 09 '22 19:10

Joop Eggen