Exercice un

On définit la classe A1 par :

package fr.univ_paris1.mass.poo.cc3.exo1;

public class A1 {
    private int u;

    private int v;

    public A1(int u) {
	this.u = u;
	v = -1;
    }

    public void foo() {
	u = u + 1;
	if (u > 3) {
	    u = 0;
	    v = v - 1;
	}
    }

    public void bar() {
	u = u - 2;
	if (u <= 0) {
	    u = 3;
	    v = v + 1;
	}
    }

    @Override
    public String toString() {
	return v + " [" + u + "]";
    }
}

Question : quel est alors l'affichage produit par le code suivant ?

package fr.univ_paris1.mass.poo.cc3.exo1;

public class TestA1 {

    public static void main(String[] args) {
	A1 x = new A1(2);
	System.out.println(x);
	for (int i = 0; i < 2; i++) {
	    x.foo();
	}
	System.out.println(x);
	A1 y = new A1(1);
	x.bar();
	System.out.println(x + " <-> " + y);
	y.bar();
	System.out.println(x + " <-> " + y);
    }

}

L'affichage obtenu est :

-1 [2]
-2 [0]
-1 [3] <-> -1 [1]
-1 [3] <-> 0 [3]

Question : modifier la classe A1 pour rendre ses instances immuables.

Il faut donc faire en sorte de que les méthodes foo et bar ne modifient plus l'objet appelant mais renvoient au contraire un nouvel objet. Ce principe conduit à la nouvelle classe suivante :

package fr.univ_paris1.mass.poo.cc3.exo1;

public class A1Immuable {
    private int u;

    private int v;

    public A1Immuable(int u) {
	this.u = u;
	v = -1;
    }

    public A1Immuable foo() {
	A1Immuable res = new A1Immuable(u + 1);
	res.v = v;
	if (res.u > 3) {
	    res.u = 0;
	    res.v = res.v - 1;
	}
	return res;
    }

    public A1Immuable bar() {
	A1Immuable res = new A1Immuable(u - 2);
	res.v = v;
	if (res.u <= 0) {
	    res.u = 3;
	    res.v = res.v + 1;
	}
	return res;
    }

    @Override
    public String toString() {
	return v + " [" + u + "]";
    }
}

Dans cette solution, on n'utilise pas un nouveau constructeur car on se contente de modifier directement les variables u et v du nouvel objet res qui vient d'être créé. On pourrait bien sûr définir un nouveau constructeur pour ne pas avoir à recopier explicitement la valeur de this.v.

Question : Donner les modifications minimales de la méthode main pour obtenir le même affichage avec les objets immuables.

On doit modifier la méthode main pour récupérer les objets renvoyés par les nouvelles méthodes. On obtient le code suivant :

package fr.univ_paris1.mass.poo.cc3.exo1;

public class TestA1Immuable {

    public static void main(String[] args) {
	A1Immuable x = new A1Immuable(2);
	System.out.println(x);
	for (int i = 0; i < 2; i++) {
	    x = x.foo();
	}
	System.out.println(x);
	A1Immuable y = new A1Immuable(1);
	x = x.bar();
	System.out.println(x + " <-> " + y);
	y = y.bar();
	System.out.println(x + " <-> " + y);
    }

}

Exercice deux

On définit la classe B1 par :

package fr.univ_paris1.mass.poo.cc3.exo2;

import java.math.BigInteger;

public class B1 {
    private BigInteger a;

    public B1() {
	a = BigInteger.ZERO;
    }

    public B1 f(int x) {
	if (x >= 0 && x <= 9) {
	    B1 r = new B1();
	    BigInteger tmp = a.multiply(BigInteger.TEN);
	    r.a = tmp.add(BigInteger.valueOf(x));
	    return r;
	} else {
	    return this;
	}
    }

    public B1 g() {
	B1 r = new B1();
	r.a = a.divide(BigInteger.TEN);
	return r;
    }

    @Override
    public String toString() {
	return a.toString();
    }
}

Question : quel est alors l'affichage produit par le code suivant ?

package fr.univ_paris1.mass.poo.cc3.exo2;

public class TestB1 {

    public static void main(String[] args) {
	B1 b = new B1();
	System.out.println(b);
	b.f(5);
	System.out.println(b);
	B1 c = b.f(5);
	System.out.println(b + " " + c);
	B1 d = c;
	c = c.f(8);
	System.out.println(c + " " + d);
	d = d.f(4).f(7);
	System.out.println(c + " " + d);
	B1 e = new B1();
	e = e.f(5);
	e = e.f(10);
	System.out.println(e);
	System.out.println(e.g() + " " + e);
    }

}

L'affichage obtenu est :

0
0
0 5
58 5
58 547
5
0 5

Question : Modifier la classe B1 pour rendre ses instances modifiables en conservant un comportement comparable.

Il suffit cette fois ci de modifier les méthodes f et g en leur permettant de changer le contenu de a. Ce principe conduit à la nouvelle classe suivante :

package fr.univ_paris1.mass.poo.cc3.exo2;

import java.math.BigInteger;

public class B1Modifiable {
    private BigInteger a;

    public B1Modifiable() {
	a = BigInteger.ZERO;
    }

    public void f(int x) {
	if (x >= 0 && x <= 9) {
	    BigInteger tmp = a.multiply(BigInteger.TEN);
	    a = tmp.add(BigInteger.valueOf(x));
	}
    }

    public void g() {
	a = a.divide(BigInteger.TEN);
    }

    @Override
    public String toString() {
	return a.toString();
    }
}

Exercice trois

On définit la classe C1 par :

package fr.univ_paris1.mass.poo.cc3.exo3;

public class C1 {
    private String bli;

    public C1(String bli) {
	this.bli = bli;
    }

    public void foo() {
	bli = "";
    }

    public String bar(String u) {
	String r = bli;
	bli = u;
	return r;
    }

}

Question : Donner un exemple de programme simple qui montre que les instances de C1 sont modifiables.

Il suffit de construire un programme montrant que le comportement d'une instance de C1 change après l'appel d'une méthode. Ici, la méthode bar renvoie le contenu de la variable bli avant l'appel, tout en modifiant sa valeur. Il suffit donc d'appeler deux fois de suite la méthode pour constater une modification du résultat, comme dans le programme suivant :

package fr.univ_paris1.mass.poo.cc3.exo3;

public class TestC1 {

    public static void main(String[] args) {
	C1 x = new C1("Toto");
	System.out.println(x.bar("Titi"));
	System.out.println(x.bar("Titi"));
    }

}

qui affiche :

Toto
Titi

Exercice quatre

On définit la classe D1 par :

package fr.univ_paris1.mass.poo.cc3.exo4;

public class D1 {
    private double z;

    public D1(double z) {
	this.z = z;
    }

    public void inc() {
	z = z + 2.0;
    }
}

Question : Expliquer brièvement pourquoi les objets de la classe D1 sont immuables.

On constate qu'aucune méthode ne permet de déterminer le contenu de l'objet (c'est-à-dire celui de la variable z). De ce fait, rien ne permet de constater un éventuel changement de comportement après un appel de méthode, ce qui rend l'objet immuable du point de vue de l'utilisateur.

Question : Ajouter une méthode (simple) rendant modifiables les objets de la classe D1.

Il suffit d'ajouter une méthode permettant d'accéder au contenu de la variable z, comme par exemple :

public double getZ() {
  return z;
}

Exercice cinq

On définit la classe E1 par :

package fr.univ_paris1.mass.poo.cc3.exo5;

import java.util.Arrays;

public class E1 {
    private char[] x;

    public E1() {
	x = new char[0];
    }

    public int plop(String s) {
	if (s.length() > 0) {
	    if (x.length == 4) {
		x = new char[] { s.charAt(0) };
		return 1;
	    } else {
		char[] r = new char[x.length + 1];
		for (int i = 0; i < x.length; i++) {
		    r[i] = x[i];
		}
		r[x.length] = s.charAt(0);
		x = r;
		return r.length;
	    }
	} else {
	    return -1;
	}
    }

    @Override
    public String toString() {
	return Arrays.toString(x);
    }
}

Question : quel est alors l'affichage produit par le code suivant ?

package fr.univ_paris1.mass.poo.cc3.exo5;

public class TestE1 {

    public static void main(String[] args) {
	E1 u = new E1();
	u.plop("Toto");
	System.out.println(u);
	System.out.println(u.plop("Ututu"));
	System.out.println(u.plop("Lock"));
	System.out.println(u);
	u.plop("plop");
	System.out.println(u.plop(""));
	u.plop("clop");
	System.out.println(u);
    }

}

L'affichage obtenu est :

[T]
2
3
[T, U, L]
-1
[c]