Exercice un

Question : quel est l'affichage produit par le programme suivant :

class Exo11 {
    private boolean flip;

    private int foo;

    public Exo11(int foo) {
	this.foo = foo;
	flip = foo > 0;
    }

    public Exo11(int foo, boolean flip) {
	this.foo = foo;
	this.flip = flip;
    }

    public String toString() {
	return flip + " [" + foo + "]";
    }

    public Exo11 flop() {
	return new Exo11(-foo);
    }

    public Exo11 comb(Exo11 that) {
	// && désigne le et logique
	return new Exo11(foo - that.foo, flip && that.flip);
    }
}

public class TestExo11 {

    public static void main(String[] args) {
	Exo11 x = new Exo11(2);
	System.out.println(x);
	Exo11 y = new Exo11(-2, true);
	System.out.println(y.toString());
	System.out.println(x.flop());
	Exo11 z = y;
	y = z.comb(y);
	System.out.println(z);
	System.out.println(y);
	Exo11 w = new Exo11(3, false);
	System.out.println(w.flop().flop());
	System.out.println((new Exo11(3)).comb(x).flop());
    }

}

On obtient l'affichage suivant :

true [2]
true [-2]
false [-2]
true [-2]
true [0]
true [3]
false [-1]

Exercice deux

Question : quel est l'affichage produit par le programme suivant :

class Exo21 {
    private String texte;

    public Exo21() {
	texte = "";
    }

    public void bla() {
	if (texte.length() > 0) {
	    // s.substring(1) renvoie une nouvelle chaîne
	    // obtenue en supprimant le premier caractère de s
	    // par ex. "Toto".substring(1) donne "oto"
	    texte = texte.substring(1);
	}
    }

    public void foo() {
	texte = texte + String.valueOf(2 * texte.length());
    }

    public String toString() {
	return "<- " + texte + " ->";
    }
}

public class TestExo21 {

    public static void main(String[] args) {
	Exo21 a = new Exo21();
	System.out.println(a);
	a.foo();
	System.out.println(a);
	a.foo();
	System.out.println(a);
	Exo21 b = a;
	String s = b.toString();
	a.bla();
	System.out.println(a);
	System.out.println(b);
	System.out.println(s);
	Exo21 c = new Exo21();
	for (int i = 0; i < 6; i++) {
	    c.foo();
	}
	System.out.println(c);
    }

}

On obtient l'affichage suivant :

<-  ->
<- 0 ->
<- 02 ->
<- 2 ->
<- 2 ->
<- 02 ->
<- 0246810 ->

Question : Modifiez la classe Exo21 pour rendre ses instances immuables en gardant le même comportement général. Vous pouvez vous contenter d'écrire les parties modifiées, sans recopier ce qui ne change pas.

Les méthodes foo modifient l'objet appelant car elles changent la chaîne de caractères référencée par la variable texte. Pour rendre les instances immuables, il suffit de modifier ces méthodes pour qu'elles renvoient un nouvel objet Exo21. Ce sont les deux seules méthodes qui modifient les instances et ce sont donc les seules à modifier. On obtient la classe suivante :

public  class Exo21Immuable {
    private String texte;

    public Exo21Immuable() {
	texte = "";
    }

    public Exo21Immuable bla() {
	if (texte.length() > 0) {
	    Exo21Immuable res = new Exo21Immuable();
	    res.texte = texte.substring(1);
	    return res;
	} else {
	    return this;
	}
    }

    public Exo21Immuable foo() {
	Exo21Immuable res = new Exo21Immuable();
	res.texte = texte + String.valueOf(2 * texte.length());
	return res;
    }

    public String toString() {
	return "<- " + texte + " ->";
    }
}
Attention, cette nouvelle version ne donne pas les mêmes résultats que la première si on ne modifie pas la méthode main.

Exercice trois

Question : quel est l'affichage produit par le programme suivant :

class Exo31 {
    private boolean[] tab;

    public Exo31(int k) {
	// les cases du tableau contiendront toutes false
	tab = new boolean[k];
    }

    public void foo(int l) {
	// ! est la négation logique
	tab[l] = !tab[l];
    }

    public int bar() {
	int n = 0;
	for (boolean b : tab) {
	    if (b) {
		n++;
	    }
	}
	return n;
    }

    public String toString() {
	StringBuilder sb = new StringBuilder(tab.length + 2);
	sb.append('[');
	for (int i = 0; i < tab.length; i++) {
	    if (tab[i]) {
		sb.append("X");
	    } else {
		sb.append(".");
	    }
	}
	sb.append("]");
	return sb.toString();
    }
}

public class TestExo31 {

    public static void main(String[] args) {
	Exo31 x = new Exo31(4);
	System.out.println(x);
	x.foo(2);
	System.out.println(x);
	System.out.println(x.bar());
	Exo31 y = x;
	for (int i = 0; i < 4; i++) {
	    x.foo(i);
	    System.out.println(y.bar());
	    if (i % 2 == 0) {
		y.foo(i);
	    }
	}
	System.out.println(x);
	System.out.println(y);
    }

}

On obtient l'affichage suivant :

[....]
[..X.]
1
2
2
1
3
[.XXX]
[.XXX]

Question : Quelle(s) méthode(s) doit-on modifier pour rendre immuables les instances de la classe Exo31 ? (Donnez une justification brève.)

On constate que seule la méthode foo modifie les instances par l'intermédiaire d'une modification d'une case du tableau tab. Il faudrait donc modifier cette méthode pour rendre les instances immuables.

Exercice quatre

Question : quel est l'affichage produit par le programme suivant :

class A1 {
    public int f(int x) {
	return 2 * x;
    }
}

class B1 extends A1 {
    @Override
    public String toString() {
	return "[B]";
    }
}

class C1 extends B1 {
    @Override
    public int f(int x) {
	return x + 3;
    }
}

public class TestABC1 {

    public static void main(String[] args) {
	A1 a = new A1();
	B1 b = new B1();
	C1 c = new C1();
	System.out.println(a + " -- " + b + " -- " + c);
	System.out.println(a.f(2) + " -- " + b.f(4));
	System.out.println(c.f(6));
	Object oa = a;
	Object ob = b;
	Object oc = c;
	System.out.println(oa + " -- " + ob + " -- " + oc);
	a = b;
	b = c;
	System.out.println(a + " -- " + b);
	System.out.println(a.f(1) + " -- " + b.f(3));
	// System.out.println(oa.f(2));
    }

}

On obtient l'affichage suivant :

A1@4130fafb -- [B] -- [B]
4 -- 8
9
A1@4130fafb -- [B] -- [B]
[B] -- [B]
2 -- 6

Question : la dernière ligne du programme ne compile pas. Expliquez brièvement pourquoi.

Dans cette ligne, oa est une variable de type Object. Au moment de la compilation, le compilateur ne peut pas connaître le type de l'objet référencé par la variable. De ce fait, seules les méthodes définies par la classe Object sont utilisables. Comme la méthode f est définie par la classe A1 et n'est donc pas utilisable par l'intermédiaire de la variable oa.

Exercice cinq

Un objet de la classe Suite ci-dessous représente des suites \((u_n)_{n\geq 0}\) définies par récurrence par la relation \(u_{n}=au_{n-1}+b\), pour tout \(n>0\). L'objectif de l'exercice est de compléter la classe de la façon suivante :

  • Les coefficients \(a\) et \(b\) de la relation sont fixés par le constructeur et stockés dans les variables a et b de l'objet.
  • La méthode valeur doit renvoyer la valeur de numéro n de la suite définie par l'objet appelant (\(u_{n}\)) appliquant la relation de récurrence et en prenant \(u_0=\) u0. On n'utilisera pas la méthode valeurs pour programmer valeur (attention au « s » !).
  • La méthode pseudoProduit doit renvoyer un nouvel objet Suite dont les coefficients sont le produit par le paramètre x des coefficients de l'objet appelant. Par exemple si la suite appelante est définie par \(a=2\) et \(b=1\), un appel à pseudoProduit avec comme paramètre 2 doit produire une suite de paramètres \(a=4\) et \(b=2\).
  • La méthode pseudoSomme doit renvoyer un nouvel objet Suite dont les coefficients \(a\) et \(b\) sont la somme des coefficients correspondants de la suite appelant et de la suite paramètres that.
  • La méthode valeurs doit renvoyer l'ensemble des n+1 premiers termes de la suite représentée par l'objet appelant de \(u_0=\) u0 à \(u_{n}\). On n'utilisera pas la méthode valeur pour programmer valeurs (attention au « s » !).
public class Suite {
    private double a;  
    private double b;

    public Suite(double a, double b) {
	// À COMPLÉTER
    }

    public double valeur(double u0, int n) {
	// À COMPLÉTER
    }

    public Suite pseudoProduit(double x) {
	// À COMPLÉTER
    }

    public Suite pseudoSomme(Suite that) {
	// À COMPLÉTER
    }

    public double[] valeurs(double u0, int n) {
	// À COMPLÉTER
    }
}

Une solution possible est donnée par la classe suivante :

public class Suite {
    private double a;

    private double b;

    public Suite(double a, double b) {
	this.a = a;
	this.b = b;
    }

    public double valeur(double u0, int n) {
	double u = u0;
	for (int i = 1; i <= n; i++) {
	    u = a * u + b;
	}
	return u;
    }

    public Suite pseudoProduit(double x) {
	return new Suite(a * x, b * x);
    }

    public Suite pseudoSomme(Suite that) {
	return new Suite(a + that.a, b + that.b);
    }

    public double[] valeurs(double u0, int n) {
	double[] res = new double[n+1];
	res[0] = u0;
	for (int i = 1; i <= n; i++) {
	    res[i] = a * res[i-1] + b;
	}
	return res;
    }
}