1

Salut,

Ça fait un moment que je me demande pourquoi on est forcé d'ajouter le case "default" dans un switch à base d'enum quand tous les cas sont gérés...
Par exemple le code suivant ne compile pas...

public class Main
{
	public static enum Solver
	{
	    ABAQUS, CAST3M ;
	}

	public static void main( String[] args )
	{
		int value ;
		Solver solver= Solver.ABAQUS ;
		switch ( solver )  {
			case ABAQUS:
				value= 0 ;
				break;
			
			case CAST3M:
				value= 1 ;
				break ;
		}
		System.out.println(value) ;
	}
}



Est ce que vous savez pourquoi ??
Merci d'avance wink

2

au pif, parce qu'il a pas moyen de savoir que tu gères tous les cas? (pourquoi je sais pas)

ou alors parce qu'ils ont pas changé l'implémentation de switch depuis longtemps oui

3

bref, ça doit marcher pour le J2SE 2 aussi, donc compatibilité précédente toussa...

4

je ne sais pas mais ça m'ennuie de faire ça:
public class Main
{
	public static enum Solver
	{
	    ABAQUS, CAST3M ;
	}

	public static void main( String[] args )
	{
		int value ;
		Solver solver= Solver.ABAQUS ;
		switch ( solver )  {
			case ABAQUS:
				value= 0 ;
				break;
			
			case CAST3M:
				value= 1 ;
				break ;
				
			default:
				throw new AssertionError() ;
		}
		System.out.println(value) ;
	}
}



mourn cry

5

et pourquoi pas
public class Main
{
	public static enum Solver
	{
	    ABAQUS, CAST3M ;
	}

	public static void main( String[] args )
	{
		int value ;
		Solver solver= Solver.ABAQUS ;
		switch ( solver )  {
			case ABAQUS:
				value= 0 ;
				break;
			default:
				value= 1 ;
				break ;
		}
		System.out.println(value) ;
	}
}


ou même
public class Main
{
	public static enum Solver
	{
	    ABAQUS, CAST3M ;
	}

	public static void main( String[] args )
	{
		int value ;
		Solver solver= Solver.ABAQUS ;
		if (solver == ABAQUS) { value = 0 ; }
		else { value = 1 ; }
		System.out.println(value) ;
	}
}


?
avatar
<<< Kernel Extremis©®™ >>> et Inventeur de la différence administratif/judiciaire ! (©Yoshi Noir)

<Vertyos> un poil plus mais elle suce bien quand même la mienne ^^
<Sabrina`> tinkiete flan c juste qu'ils sont jaloux que je te trouve aussi appétissant

6

les 3 me plaisent, mais je préfère l'assertion hehe programmation défensive, etc...

7

default:
  value=31337;

gni
avatar
Mes news pour calculatrices TI: Ti-Gen
Mes projets PC pour calculatrices TI: TIGCC, CalcForge (CalcForgeLP, Emu-TIGCC)
Mes chans IRC: #tigcc et #inspired sur irc.freequest.net (UTF-8)

Liberté, Égalité, Fraternité

8

Flanker: Tes version sont pas equivalentes au code de Pen^2. Tu assumes que tu as forcement une valeur correcte comme entree, ce qui peut ne pas etre le cas.

9

nEUrOO > justement, il a envie de se débarasser du default, non ? hum
avatar
<<< Kernel Extremis©®™ >>> et Inventeur de la différence administratif/judiciaire ! (©Yoshi Noir)

<Vertyos> un poil plus mais elle suce bien quand même la mienne ^^
<Sabrina`> tinkiete flan c juste qu'ils sont jaloux que je te trouve aussi appétissant

10

Je pense qu'il voulait automatiquement une exception par contre... sinon, le switch ne sert a rien smile

11

nEUrOO > À première vue, je dirais que tu bosses dans la sécurité toi non ? grin
Il peut très bien être sûr de n'appeler cette fonction dans le programme qu'avec des valeurs adéquatement construites et ne pas polluer son code happy, et le switch est la structure de contrôle à utiliser pour un filtrage de cas.
avatar
fabetal_ > Hier, je me suis fait monter par un pote
redangel > et en chevals, ça donne quoi?
Nil> OMG I think I'm gay

12

Tout de suite les prejuges wink
Oui, je suis d'accord sinon... mais pas assez d'information pour savoir cela

13

BookeldOr (./11) :
Il peut très bien être sûr de n'appeler cette fonction dans le programme qu'avec des valeurs adéquatement construites et ne pas polluer son code smile2.gif

En fait, je ne vois justement pas comment on peut passer autre chose qu'une valeur valide de l'enum... (même volontairement) confus



En C++, on peut effectivement faire un truc du style :
#include <iostream>

enum Solver { ABAQUS= 0, CAST3M= 1 } ;

int main()
{
	int value ;
	enum Solver solver= static_cast<enum Solver>(0xDEAD) ;
	switch ( solver ) {
		case ABAQUS:
			value= 0 ;
			break ;
		case CAST3M:
			value= 1 ;
			break ;
	}
	std::cout << "value==" << value << std::endl ;
}



Mais en JAVA, un code du genre de celui ci :
public class Main
{
	public static enum Solver
	{
		ABAQUS, CAST3M ;
	}

	public static void main( String[] args )
	{
		int value ;
		Solver solver= (Solver)new Integer(0xDEAD) ;
		switch ( solver ) {
			case ABAQUS:
				value= 0 ;
				break ;

			case CAST3M:
				value= 1 ;
				break ;

			default:
				throw new AssertionError() ;
		}
		System.out.println(value) ;
	}
}


ne compile même pas, donc je ne vois pas l'intérêt de forcer le default ! (il est possible qu'on puisse le faire quand même en fait, je n'en sais rien...)



Flanker > bof, oui, m'enfin c'est nettement moins lisible... embarrassed (pour l'instant je pars du principe qu'on ne peut pas passer autre chose que l'enum prévu et que c'est donc bien équivalent)
Tu me diras, pour améliorer la lisibilité, rien n'empêche d'écrire :
public class Main
{
	public static enum Solver
	{
		ABAQUS, CAST3M ;
	}

	public static void main( String[] args )
	{
		int value ;
		Solver solver= Solver.CAST3M ;
		switch ( solver ) {
			case ABAQUS:
				value= 0 ;
				break ;

			case CAST3M:
			default:
				value= 1 ;
				break ;
		}
		System.out.println(value) ;
	}
}

mais bon c'est pas terrible non plus dans la mesure où je ne vois toujours pas comment on peut passer autre chose qu'une valeur valide de l'enum confus

14

tiens, comme ça, ça compile (devil) :

public class Main
{
	public static enum Solver
	{
		ABAQUS, CAST3M ;
	}

	public static void main( String[] args )
	{
		int value ;
		Solver solver= (Solver)((Object)new Integer(0xDEAD)) ;
		switch ( solver ) {
			case ABAQUS:
				value= 0 ;
				break ;

			case CAST3M:
				value= 1 ;
				break ;

			default:
				throw new AssertionError() ;
		}
		System.out.println(value) ;
	}
}



Mais ça déclenche une ClassCastException au moment de l'affectation, donc... Je ne vois toujours pas l'intérêt du default confus

(sur ce je vais me coucher, 'ne nuit zzz)




PS : plus ça va, plus j'ai le sentiment qu'il faudrait déclencher une InternalError à la place d'une AssertionError en fait embarrassed

15

Peut être qu'avec la reflection on peut modifier l'enum remarquez. Il faudrait que je regarde.

16

ah ben ça se peut oui

mais toute façon un enum est une simple classe pleine de public static final int, non?

17

public enum Solver
{
	ABAQUS, CAST3M ; 
}


correspond à :
// Decompiled by Jad v1.5.8g. Copyright 2001 Pavel Kouznetsov.
// Jad home page: http://www.kpdus.com/jad.html
// Decompiler options: packimports(3) 
// Source File Name:   Solver.java


public final class Solver extends Enum
{

    public static final Solver[] values()
    {
        return (Solver[])$VALUES.clone();
    }

    public static Solver valueOf(String s)
    {
        return (Solver)Enum.valueOf(Solver, s);
    }

    private Solver(String s, int i)
    {
        super(s, i);
    }

    public static final Solver ABAQUS;
    public static final Solver CAST3M;
    private static final Solver $VALUES[];

    static 
    {
        ABAQUS = new Solver("ABAQUS", 0);
        CAST3M = new Solver("CAST3M", 1);
        $VALUES = (new Solver[] {
            ABAQUS, CAST3M
        });
    }
}



donc non, une entrée d'un enum n'est pas un int.


(enfin bon, ça n'empeche qu'il y a bien un
private final int ordinal;
dans Enum.java, mais bon.. embarrassed)



ps : d'ailleurs c'est grâce au fait que ce n'est pas qu'un int qu'on peut faire des choses comme System.out.println(Solver.ABAQUS) et que ça écrit ABAQUS et pas un nombre qui ne veut rien dire hehe

18

squalyl (./16) :
mais toute façon un enum est une simple classe pleine de public static final int, non?
Non les énum en Java sont en fait des classe dont peut s'en servir pour définir bien plus de choses que de simples constantes.
Pen^2 (./15) :
Peut être qu'avec la reflection on peut modifier l'enum remarquez. Il faudrait que je regarde.
C'est ce que je pensains, mais au vu de ce que tu as décompilé, ce n'est même pas possible(classe final).

avatar

19

BookeldOr (./11) :
nEUrOO > À première vue, je dirais que tu bosses dans la sécurité toi non ? grin
Il peut très bien être sûr de n'appeler cette fonction dans le programme qu'avec des valeurs adéquatement construites et ne pas polluer son code happy, et le switch est la structure de contrôle à utiliser pour un filtrage de cas.

Cela s'appelle la programmation par contrat de Bertrand Meyer.
Post, Pre condition, et invariant de classe.
Il faut definir les responsabilités pour l'appelant et l'appelé.
en gros:
int squareRoot(int x){
REQUIRE(x>=0);
.. calcule de la racine carrée ...
ENSURE(ret >=0 && ret * ret == x);
}


Cette technique permet de reduire considérablement la complexité cyclomatique d'un programme, elle améliore donc la qualité du logiciel

20

c'est pas une metrique. la complexite cyclomatique en est une. mais une technique est une technique.
anyway, c'est une bonne technique en tout cas wink

21

oui ma phrase est mal tournée mais je voulais bien entendu dire le nombre cyclomatique wink
[c'est corrigé]

22

JackosKing (./19) :
Cela s'appelle la programmation par contrat de Bertrand Meyer. [...]

Je suppose que tu voulais compléter ça :
BookeldOr (./11) :
Il peut très bien être sûr de n'appeler cette fonction dans le programme qu'avec des valeurs adéquatement construites

Sinon, je ne vois pas trop où tu veux en venir...

En fait, je répondais à
nEUrOO (./8) :
Flanker: Tes version sont pas equivalentes au code de Pen^2. Tu assumes que tu as forcement une valeur correcte comme entree, ce qui peut ne pas etre le cas.

C'était juste pour dire (de manière certes trollatoire) que parfois on peut être certain d'appeler une fonction avec la bonne valeur partout dans son code et que mettre ces vérifications à la Hoare à la main partout n'est pas forcément nécessaire et parfois ne fait que polluer le code et le rendre illisible avec un tas d'assertions partout.
Par exemple, on peut très bien passer une analyse statique ensuite qui vérifie, pour te faire plaisir avec une sémantique axiomatique, que les valeurs passées sont bien les bonnes, sans pour autant ajouter des tests dynamiques ou des annotations explicites partout.

Ceci dit, la décompilation du ./17 montre que les enums sont bien des types algébriques non extensibles qui sont vérifiés dynamiquement avec une instruction JVM checkcast cf ./14, donc je ne vois toujours pas pourquoi ce default est nécessaire...
avatar
fabetal_ > Hier, je me suis fait monter par un pote
redangel > et en chevals, ça donne quoi?
Nil> OMG I think I'm gay

23

tout a fait d'accord avec toi smile

24

16> joli smile

sinon moi je pense que c'est un problème de l'instruction switch, qui a pas du évoluer depuis java 2.

25

Bon, j'ai voulu faire un test avec ça :

public class DefineAdt {
    public enum Adt { CONS0, CONS1, CONS2 } ;

    public Adt get () {
	return Adt.CONS2 ;
    }
}


public class UseAdt {
    public static void main (String[] argv) {
	DefineAdt x = new DefineAdt () ;
	String value = "NOT SET";
	switch (x.get ()) {
	case CONS0 :
	    value = "CONS0" ;
	    break ;
	case CONS1 :
	    value = "CONS1" ;
	    break ;
	}
	System.out.println ("Got " + value) ;
    }
}


Mais ça compile nickel, sans warning, et affiche simplement "Got NOT SET", y'a des flags à mettre ?

benjamin@benjamin-laptop:~/Work/Stuff/java_adt$ java -version
java version "1.6.0_03"
Java(TM) SE Runtime Environment (build 1.6.0_03-b05)
Java HotSpot(TM) Server VM (build 1.6.0_03-b05, mixed mode)


edit : Oh, j'avais mal lu, J2SE5, moi j'ai le 6
avatar
fabetal_ > Hier, je me suis fait monter par un pote
redangel > et en chevals, ça donne quoi?
Nil> OMG I think I'm gay

26

C'est très malin d'ailleurs, car un prog correct pour le 5 marche de la même façon avec le 6, mais le 6 ajoute un comportement cohérent avec le switch sur les entiers :
        int i = Integer.parseInt (argv[0]) ;
        switch (i) {
        case 1 :
        case 2 :
            System.out.println ("cas géré : " + i) ;
        }

Qui ne nécessite pas de gérer tous les cas.

Donc en gros, on passe d'un filtrage exhaustif de types algébriques à un switch à la C, ce qui est àmha plus cohérent avec le modèle de Java.
avatar
fabetal_ > Hier, je me suis fait monter par un pote
redangel > et en chevals, ça donne quoi?
Nil> OMG I think I'm gay

27

BookeldOr (./22) :
C'était juste pour dire (de manière certes trollatoire) que parfois on peut être certain d'appeler une fonction avec la bonne valeur partout dans son code et que mettre ces vérifications à la Hoare à la main partout n'est pas forcément nécessaire et parfois ne fait que polluer le code et le rendre illisible avec un tas d'assertions partout.
Par exemple, on peut très bien passer une analyse statique ensuite qui vérifie, pour te faire plaisir avec une sémantique axiomatique, que les valeurs passées sont bien les bonnes, sans pour autant ajouter des tests dynamiques ou des annotations explicites partout.


Attention, si tu veux faire du code facilement "maintenable" portable et "refactorisable", les contrats sont indispensables.
De plus ils te permettent de définir le responsable dans l'erreur d'un programme. Les contrats ne sont pas forcément aussi simples qu'on peut le penser. Exemple pour l'échantillonnage d'un signal, il peut être utile de mettre le théorème de shanone comme condition d'entrée. Idem pour les invariants etc.
Enfin peut être qu'on ne parle pas de la même chose.

28

Ben c'est une méthode de développement pas mal, ok, mais je propose de ne pas troller sur "ma méthode c'est la plus mieux" et de se concentrer sur le problème happy
avatar
fabetal_ > Hier, je me suis fait monter par un pote
redangel > et en chevals, ça donne quoi?
Nil> OMG I think I'm gay

29

ok, ceci etant dit c'est pas ma méthode mais celle de B. Meyer smile

30

BookeldOr (./25) :
Bon, j'ai voulu faire un test avec ça :

public class DefineAdt {
    public enum Adt { CONS0, CONS1, CONS2 } ;

    public Adt get () {
	return Adt.CONS2 ;
    }
}


public class UseAdt {
    public static void main (String[] argv) {
	DefineAdt x = new DefineAdt () ;
	String value = "NOT SET";
	switch (x.get ()) {
	case CONS0 :
	    value = "CONS0" ;
	    break ;
	case CONS1 :
	    value = "CONS1" ;
	    break ;
	}
	System.out.println ("Got " + value) ;
    }
}


Mais ça compile nickel, sans warning, et affiche simplement "Got NOT SET", y'a des flags à mettre ?

benjamin@benjamin-laptop:~/Work/Stuff/java_adt$ java -version
java version "1.6.0_03"
Java(TM) SE Runtime Environment (build 1.6.0_03-b05)
Java HotSpot(TM) Server VM (build 1.6.0_03-b05, mixed mode)


edit : Oh, j'avais mal lu, J2SE5, moi j'ai le 6



En fait ton exemple compile/s'exécute très bien aussi avec le J2SE5 (VM 1.5.0_06) 1

Mon *problème* n'est pas d'être obligé de gérer tous les cas dans le switch (d'ailleurs on n'est effectivement pas obligé), mais simplement que pour le cas où justement toutes les valeurs d'un enum sont traitées explicitement, le code correspondant au default ne pourra jamais (?) être atteint, et que par conséquent c'est débile d'être obligé de le mettre pour que le compilateur considère que la variable value est initialisée dans tous les cas.

Pour moi c'est exactement comme s'il refusait de compiler
int a ;
if ( true )
  a= 1 ;
System.out.println(a) ;

en prétextant que a n'est pas initialisé dans tous les cas, et qu'on soit obligé de ruser en écrivant
int a ;
if ( true )
  a= 1 ;
else
  throw new AssertionError() ;
System.out.println(a) ;






1 Sous eclipse en fait il refuse de compiler parce que toutes les possibilités de l'enum ne sont pas gérées, mais je crois que c'est moi qui l'avait réglé comme ça y'a longtemps... Avec javac ça compile et s'exécute sans difficultés