Comment invoquer une méthode Java lorsque le nom de la méthode est donné sous forme de chaîne?


684

Si j'ai deux variables:

Object obj;
String methodName = "getName";

Sans connaître la classe de obj, comment puis-je appeler la méthode identifiée par methodNamedessus?

La méthode appelée n'a pas de paramètres et une Stringvaleur de retour. C'est un getter pour un bean Java .


3
Soit utiliser l'api de réflexion soit utiliser groovy
Peter Kelley

Réponses:


972

Le codage de la hanche, ce serait quelque chose comme:

java.lang.reflect.Method method;
try {
  method = obj.getClass().getMethod(methodName, param1.class, param2.class, ..);
} catch (SecurityException e) { ... }
  catch (NoSuchMethodException e) { ... }

Les paramètres identifient la méthode très spécifique dont vous avez besoin (s'il y a plusieurs surcharges disponibles, si la méthode n'a pas d'arguments, donnez seulement methodName).

Ensuite, vous appelez cette méthode en appelant

try {
  method.invoke(obj, arg1, arg2,...);
} catch (IllegalArgumentException e) { ... }
  catch (IllegalAccessException e) { ... }
  catch (InvocationTargetException e) { ... }

Encore une fois, omettez les arguments .invoke, si vous n'en avez pas. Mais ouais. En savoir plus sur Java Reflection


2
J'étais un peu contrarié par le fait que Java utilise l'effacement de type, mais sachant qu'au moins il a Reflection me réconforte à nouveau: D Et maintenant, avec lambdas dans Java 8, le langage se met vraiment à jour avec le développement moderne. La seule chose qui manque maintenant est le support natif des getters et setters, ou des propriétés telles qu'elles sont connues en C #.
7hi4g0

120
Pas juste -1. Henrik ne préconise probablement pas d'écraser les exceptions et n'a rien écrit pour eux parce qu'il essaie simplement de faire preuve de réflexion.
dessiné

70
Plus un pour montrer quelques exceptions potentielles. Si j'avais écrit cela, ce serait ... catch (Exception e) {...
mikbanUtah

1
Je suis « variable peut ne pas avoir été initialisé » pour l' methoden method.invoke(obj, arg1, arg2,...);. a method = null;résout le problème mais le mentionner dans la réponse n'est pas une mauvaise idée.
Amin

2
@ Les méthodes Java de DeaMon1 n'utilisent pas de "codes de sortie", mais si la méthode retourne quelque chose, invokeelle renverra tout ce qu'elle a renvoyé. Si une exception se produit lors de l'exécution de la méthode, l'exception sera encapsulée dans un InvocationTargetException.
ThePyroEagle

194

Utilisez l' invocation de méthode à partir de la réflexion:

Class<?> c = Class.forName("class name");
Method method = c.getDeclaredMethod("method name", parameterTypes);
method.invoke(objectToInvokeOn, params);

Où:

  • "class name" est le nom de la classe
  • objectToInvokeOn est de type Object et est l'objet sur lequel vous souhaitez appeler la méthode
  • "method name" est le nom de la méthode que vous souhaitez appeler
  • parameterTypesest de type Class[]et déclare les paramètres que la méthode prend
  • paramsest de type Object[]et déclare les paramètres à passer à la méthode

Cool, je pense que vous avez raison avec getDeclaredMethod (), il est probablement plus «sûr» que getMethod () ..
brasskazoo

22
Faux. Oui, getDeclaredMethod fonctionne avec des méthodes privées et protégées. MAIS: cela ne fonctionne pas avec les méthodes définies dans les superclasses (méthodes héritées). Cela dépend donc fortement de ce que vous voulez faire. Dans de nombreux cas, vous souhaitez qu'elle fonctionne quelle que soit la classe exacte dans laquelle la méthode est définie.
jrudolph

Et où dois-je mettre le fichier "classe"? expliquer de préférence pour Eclipse IDE
Dr.jacky

@ Mr.Hyde sur le chemin de la classe.
Stijn de Witt

Que dois-je mettre à l'intérieur de et method.invoke () si la méthode que j'appelle n'accepte aucun paramètre? Il semble que je dois encore fournir un deuxième paramètre, devrait-il s'agir d'un tableau d'objets vide?
Igor

101

Pour ceux qui veulent un exemple de code simple en Java 7:

Dog classe:

package com.mypackage.bean;

public class Dog {
    private String name;
    private int age;

    public Dog() {
        // empty constructor
    }

    public Dog(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public void printDog(String name, int age) {
        System.out.println(name + " is " + age + " year(s) old.");
    }
}

ReflectionDemo classe:

package com.mypackage.demo;

import java.lang.reflect.*;

public class ReflectionDemo {

    public static void main(String[] args) throws Exception {
        String dogClassName = "com.mypackage.bean.Dog";
        Class<?> dogClass = Class.forName(dogClassName); // convert string classname to class
        Object dog = dogClass.newInstance(); // invoke empty constructor

        String methodName = "";

        // with single parameter, return void
        methodName = "setName";
        Method setNameMethod = dog.getClass().getMethod(methodName, String.class);
        setNameMethod.invoke(dog, "Mishka"); // pass arg

        // without parameters, return string
        methodName = "getName";
        Method getNameMethod = dog.getClass().getMethod(methodName);
        String name = (String) getNameMethod.invoke(dog); // explicit cast

        // with multiple parameters
        methodName = "printDog";
        Class<?>[] paramTypes = {String.class, int.class};
        Method printDogMethod = dog.getClass().getMethod(methodName, paramTypes);
        printDogMethod.invoke(dog, name, 3); // pass args
    }
}

Production: Mishka is 3 year(s) old.


Vous pouvez appeler le constructeur avec des paramètres de cette façon:

Constructor<?> dogConstructor = dogClass.getConstructor(String.class, int.class);
Object dog = dogConstructor.newInstance("Hachiko", 10);

Vous pouvez également supprimer

String dogClassName = "com.mypackage.bean.Dog";
Class<?> dogClass = Class.forName(dogClassName);
Object dog = dogClass.newInstance();

et fait

Dog dog = new Dog();

Method method = Dog.class.getMethod(methodName, ...);
method.invoke(dog, ...);

Lecture suggérée: création de nouvelles instances de classe


1
La meilleure réponse ici. Complet et concis
Reuben JaMes Aveño Gruta

1
Bonne meilleure réponse.
Dhara Patel

Où obtenez-vous un Methodobjet?
parlad

Du paquet de réflexion.
argent

55

La méthode peut être invoquée comme ceci. Il y a aussi plus de possibilités (vérifiez l'api de réflexion), mais c'est la plus simple:

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

import org.junit.Assert;
import org.junit.Test;

public class ReflectionTest {

    private String methodName = "length";
    private String valueObject = "Some object";

    @Test
    public void testGetMethod() throws SecurityException, NoSuchMethodException, IllegalArgumentException,
            IllegalAccessException, InvocationTargetException {
        Method m = valueObject.getClass().getMethod(methodName, new Class[] {});
        Object ret = m.invoke(valueObject, new Object[] {});
        Assert.assertEquals(11, ret);
    }



}

7
+1 pour la seule réponse qui reconnaissait que l'OP ne spécifiait "aucun paramètre" dans sa question (et parce que c'était aussi ce que je cherchais).
John Fitzpatrick

16

Tout d'abord, ne le faites pas. Évitez ce type de code. Il a tendance à être très mauvais et non sécurisé (voir la section 6 des directives de codage sécurisé pour le langage de programmation Java, version 2.0 ).

Si vous devez le faire, préférez java.beans à la réflexion. Les haricots enveloppent la réflexion permettant un accès relativement sûr et conventionnel.


11
Je ne suis pas d'accord. Il est très facile d'écrire un tel code pour être sécurisé et je l'ai fait dans plusieurs langues. Par exemple, on pourrait créer un ensemble de méthodes autorisées et autoriser une méthode à être invoquée uniquement si son nom figure dans l'ensemble. Encore plus sûr (mais toujours simple) serait de limiter chaque méthode autorisée à un état spécifique et de ne pas invoquer la méthode à moins que le thread / l'interface / l'utilisateur / quoi que ce soit correspond à ces critères.
JSON

Ne soyez jamais aussi catégorique sur de telles questions. En ce moment, je crée un programme simple pour permettre à l'utilisateur de définir des tâches arbitraires sur des objets arbitraires à l'aide d'interfaces Web. Je sais que c'est, en effet, peu sûr, mais des tests appropriés sont effectués une fois la configuration reçue, et cela permet à un non-programmeur de configurer facilement les tâches, et donne également aux programmes la possibilité de lier des classes personnalisées au code générique (c'est la partie j'utilise la réflexion pour, afin de leur permettre de configurer les méthodes à utiliser via l'interface web) sans avoir à mettre à jour l'interface graphique.
DGoiko

14

Pour compléter les réponses de mon collègue, vous voudrez peut-être porter une attention particulière à:

  • appels statiques ou d'instance (dans un cas, vous n'avez pas besoin d'une instance de la classe, dans l'autre, vous devrez peut-être compter sur un constructeur par défaut existant qui peut ou non être là)
  • appel de méthode public ou non public (pour ce dernier, vous devez appeler setAccessible sur la méthode dans un bloc doPrivileged , d'autres findbugs ne seront pas satisfaits )
  • encapsulation dans une exception applicative gérable de plus si vous souhaitez annuler les nombreuses exceptions du système java (d'où l'exception CCException dans le code ci-dessous)

Voici un ancien code java1.4 qui prend en compte ces points:

/**
 * Allow for instance call, avoiding certain class circular dependencies. <br />
 * Calls even private method if java Security allows it.
 * @param aninstance instance on which method is invoked (if null, static call)
 * @param classname name of the class containing the method 
 * (can be null - ignored, actually - if instance if provided, must be provided if static call)
 * @param amethodname name of the method to invoke
 * @param parameterTypes array of Classes
 * @param parameters array of Object
 * @return resulting Object
 * @throws CCException if any problem
 */
public static Object reflectionCall(final Object aninstance, final String classname, final String amethodname, final Class[] parameterTypes, final Object[] parameters) throws CCException
{
    Object res;// = null;
    try {
        Class aclass;// = null;
        if(aninstance == null)
        {
            aclass = Class.forName(classname);
        }
        else
        {
            aclass = aninstance.getClass();
        }
        //Class[] parameterTypes = new Class[]{String[].class};
    final Method amethod = aclass.getDeclaredMethod(amethodname, parameterTypes);
        AccessController.doPrivileged(new PrivilegedAction() {
    public Object run() {
                amethod.setAccessible(true);
                return null; // nothing to return
            }
        });
        res = amethod.invoke(aninstance, parameters);
    } catch (final ClassNotFoundException e) {
        throw new CCException.Error(PROBLEM_TO_ACCESS+classname+CLASS, e);
    } catch (final SecurityException e) {
        throw new CCException.Error(PROBLEM_TO_ACCESS+classname+GenericConstants.HASH_DIESE+ amethodname + METHOD_SECURITY_ISSUE, e);
    } catch (final NoSuchMethodException e) {
        throw new CCException.Error(PROBLEM_TO_ACCESS+classname+GenericConstants.HASH_DIESE+ amethodname + METHOD_NOT_FOUND, e);
    } catch (final IllegalArgumentException e) {
        throw new CCException.Error(PROBLEM_TO_ACCESS+classname+GenericConstants.HASH_DIESE+ amethodname + METHOD_ILLEGAL_ARGUMENTS+String.valueOf(parameters)+GenericConstants.CLOSING_ROUND_BRACKET, e);
    } catch (final IllegalAccessException e) {
        throw new CCException.Error(PROBLEM_TO_ACCESS+classname+GenericConstants.HASH_DIESE+ amethodname + METHOD_ACCESS_RESTRICTION, e);
    } catch (final InvocationTargetException e) {
    throw new CCException.Error(PROBLEM_TO_ACCESS+classname+GenericConstants.HASH_DIESE+ amethodname + METHOD_INVOCATION_ISSUE, e);
    } 
    return res;
}

12
Object obj;

Method method = obj.getClass().getMethod("methodName", null);

method.invoke(obj, null);

L'objet doit avoir au moins valeur / valeurs.
Lova Chittumuri

Cela a très bien fonctionné pour ce dont j'avais besoin. J'avais une classe qui était déjà instanciée et dont j'avais juste besoin pour en retirer une méthode. Ajouter des captures pour les exceptions est une bonne idée ici, mais sinon, cela a parfaitement fonctionné pour moi. Je pense que ma façon d'éviter les exceptions nulles était d'utiliser des nullables, mais j'utilisais une plage très restreinte de noms de méthode (littéralement juste un compteur de 1 à 4).
Jain Waldrip

12
//Step1 - Using string funClass to convert to class
String funClass = "package.myclass";
Class c = Class.forName(funClass);

//Step2 - instantiate an object of the class abov
Object o = c.newInstance();
//Prepare array of the arguments that your function accepts, lets say only one string here
Class[] paramTypes = new Class[1];
paramTypes[0]=String.class;
String methodName = "mymethod";
//Instantiate an object of type method that returns you method name
 Method m = c.getDeclaredMethod(methodName, paramTypes);
//invoke method with actual params
m.invoke(o, "testparam");

8

Si vous effectuez l'appel plusieurs fois, vous pouvez utiliser les nouveaux descripteurs de méthode introduits dans Java 7. Ici, nous allons pour votre méthode renvoyant une chaîne:

Object obj = new Point( 100, 200 );
String methodName = "toString";  
Class<String> resultType = String.class;

MethodType mt = MethodType.methodType( resultType );
MethodHandle methodHandle = MethodHandles.lookup().findVirtual( obj.getClass(), methodName, mt );
String result = resultType.cast( methodHandle.invoke( obj ) );

System.out.println( result );  // java.awt.Point[x=100,y=200]

1
Aux futurs lecteurs; Si vous vous souciez de la performance, vous voudrez l'utiliser invokeExactchaque fois que vous le pourrez. Pour cela, la signature du site d'appel doit correspondre exactement au type de handle de méthode. Il faut généralement un peu de bricolage pour se rendre au travail. Dans ce cas, vous devez lancer le premier paramètre avec: methodHandle = methodHandle.asType(methodHandle.type().changeParameterType(0, Object.class));puis invoquer commeString result = (String) methodHandle.invokeExact(obj);
Jorn Vernee

7

Cela ressemble à quelque chose qui est faisable avec le package Java Reflection.

http://java.sun.com/developer/technicalArticles/ALT/Reflection/index.html

Particulièrement sous Invocation de méthodes par nom:

import java.lang.reflect. *;

public class method2 {
  public int add(int a, int b)
  {
     return a + b;
  }

  public static void main(String args[])
  {
     try {
       Class cls = Class.forName("method2");
       Class partypes[] = new Class[2];
        partypes[0] = Integer.TYPE;
        partypes[1] = Integer.TYPE;
        Method meth = cls.getMethod(
          "add", partypes);
        method2 methobj = new method2();
        Object arglist[] = new Object[2];
        arglist[0] = new Integer(37);
        arglist[1] = new Integer(47);
        Object retobj 
          = meth.invoke(methobj, arglist);
        Integer retval = (Integer)retobj;
        System.out.println(retval.intValue());
     }
     catch (Throwable e) {
        System.err.println(e);
     }
  }
}

6

Indexation (plus rapide)

Vous pouvez utiliser FunctionalInterfacepour enregistrer des méthodes dans un conteneur pour les indexer. Vous pouvez utiliser un conteneur de tableaux pour les appeler par des nombres ou une table de hachage pour les appeler par des chaînes. Par cette astuce, vous pouvez indexer vos méthodes pour les invoquer dynamiquement plus rapidement .

@FunctionalInterface
public interface Method {
    double execute(int number);
}

public class ShapeArea {
    private final static double PI = 3.14;

    private Method[] methods = {
        this::square,
        this::circle
    };

    private double square(int number) {
        return number * number;
    }

    private double circle(int number) {
        return PI * number * number;
    }

    public double run(int methodIndex, int number) {
        return methods[methodIndex].execute(aNumber);
    }
}

Syntaxe lambda

Vous pouvez également utiliser la syntaxe lambda:

public class ShapeArea {
    private final static double PI = 3.14;

    private Method[] methods = {
        number -> {
            return number * number;
        },
        number -> {
            return PI * number * number;
        },
    };

    public double run(int methodIndex, int number) {
        return methods[methodIndex].execute(aNumber);
    }
}

1
Cette technique semble bien meilleure que la réflexion.
John O

Est-ce vraiment beaucoup mieux?
Dimitri Kopriwa

@DimitriKopriwa Indexing est la façon dont vous utilisez RAM au lieu des calculs CPU. Pour l'indexation entière, la difficulté de l'algorithme est O(1).
Amir Fo

5
Method method = someVariable.class.getMethod(SomeClass);
String status = (String) method.invoke(method);

SomeClassest la classe et someVariableest une variable.


si someVariable est vraiment un objet, appelez someVariable.getClass (). De plus, vous ne pouvez pas appeler getMethod () avec une classe comme seul argument. Ni invoquer méthode avec méthode. Correct: someVariable.getClass (). GetMethod ("coolMethod", parameterClasses) .invoke (arguments);
Orangle

5

Je fais ça comme ça:

try {
    YourClass yourClass = new YourClass();
    Method method = YourClass.class.getMethod("yourMethodName", ParameterOfThisMethod.class);
    method.invoke(yourClass, parameter);
} catch (Exception e) {
    e.printStackTrace();
}

5

Veuillez vous référer au code suivant peut vous aider.

public static Method method[];
public static MethodClass obj;
public static String testMethod="A";

public static void main(String args[]) 
{
    obj=new MethodClass();
    method=obj.getClass().getMethods();
    try
    {
        for(int i=0;i<method.length;i++)
        {
            String name=method[i].getName();
            if(name==testMethod)
            {   
                method[i].invoke(name,"Test Parameters of A");
            }
        }
    }
    catch(Exception ex)
    {
        System.out.println(ex.getMessage());
    }
}

Merci....


Ce n'est pas ainsi que vous comparez des chaînes en Java. Vous devez utiliser la méthode .equals. Sinon, vous comparez simplement qu'il s'agit de la même référence d'objet, et vous ne vous souciez pas réellement des références d'objet - juste le contenu de la chaîne étant une correspondance. Vous pouvez également obtenir la méthode par nom via la réflexion, donc vous ne savez pas pourquoi vous rouleriez la vôtre?
Lo-Tan

5

Voici les MÉTHODES PRÊTES À UTILISER:

Pour appeler une méthode, sans arguments:

public static void callMethodByName(Object object, String methodName) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {
    object.getClass().getDeclaredMethod(methodName).invoke(object);
}

Pour appeler une méthode, avec Arguments:

    public static void callMethodByName(Object object, String methodName, int i, String s) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {
        object.getClass().getDeclaredMethod(methodName, int.class, String.class).invoke(object, i, s);
    }

Utilisez les méthodes ci-dessus comme ci-dessous:

package practice;

import java.io.IOException;
import java.lang.reflect.InvocationTargetException;

public class MethodInvoke {

    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, IOException {
        String methodName1 = "methodA";
        String methodName2 = "methodB";
        MethodInvoke object = new MethodInvoke();
        callMethodByName(object, methodName1);
        callMethodByName(object, methodName2, 1, "Test");
    }

    public static void callMethodByName(Object object, String methodName) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {
        object.getClass().getDeclaredMethod(methodName).invoke(object);
    }

    public static void callMethodByName(Object object, String methodName, int i, String s) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {
        object.getClass().getDeclaredMethod(methodName, int.class, String.class).invoke(object, i, s);
    }

    void methodA() {
        System.out.println("Method A");
    }

    void methodB(int i, String s) {
        System.out.println("Method B: "+"\n\tParam1 - "+i+"\n\tParam 2 - "+s);
    }
}

Production:

Méthode A  
Méthode B:  
	Param1 - 1  
	Param 2 - Test

3

Student.java

class Student{
    int rollno;
    String name;

    void m1(int x,int y){
        System.out.println("add is" +(x+y));
    }

    private void m3(String name){
        this.name=name;
        System.out.println("danger yappa:"+name);
    }
    void m4(){
        System.out.println("This is m4");
    }
}

StudentTest.java

import java.lang.reflect.Method;
public class StudentTest{

     public static void main(String[] args){

        try{

            Class cls=Student.class;

            Student s=(Student)cls.newInstance();


            String x="kichha";
            Method mm3=cls.getDeclaredMethod("m3",String.class);
            mm3.setAccessible(true);
            mm3.invoke(s,x);

            Method mm1=cls.getDeclaredMethod("m1",int.class,int.class);
            mm1.invoke(s,10,20);

        }
        catch(Exception e){
            e.printStackTrace();
        }
     }
}

1

Vous devez utiliser la réflexion - initier un objet de classe, puis une méthode de cette classe, puis invoquer cette méthode sur un objet avec des paramètres facultatifs . N'oubliez pas d'envelopper l'extrait de code suivant dans le bloc try-catch

J'espère que cela aide!

Class<?> aClass = Class.forName(FULLY_QUALIFIED_CLASS_NAME);
Method method = aClass.getMethod(methodName, YOUR_PARAM_1.class, YOUR_PARAM_2.class);
method.invoke(OBJECT_TO_RUN_METHOD_ON, YOUR_PARAM_1, YOUR_PARAM_2);

1

Cela fonctionne bien pour moi:

public class MethodInvokerClass {
    public static void main(String[] args) throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, ClassNotFoundException, InvocationTargetException, InstantiationException {
        Class c = Class.forName(MethodInvokerClass.class.getName());
        Object o = c.newInstance();
        Class[] paramTypes = new Class[1];
        paramTypes[0]=String.class;
        String methodName = "countWord";
         Method m = c.getDeclaredMethod(methodName, paramTypes);
         m.invoke(o, "testparam");
}
public void countWord(String input){
    System.out.println("My input "+input);
}

}

Production:

My input testparam

Je peux invoquer la méthode en passant son nom à une autre méthode (comme main).


1

en utilisant import java.lang.reflect.*;

public static Object launchProcess(String className, String methodName, Class<?>[] argsTypes, Object[] methodArgs)
        throws Exception {

    Class<?> processClass = Class.forName(className); // convert string classname to class
    Object process = processClass.newInstance(); // invoke empty constructor

    Method aMethod = process.getClass().getMethod(methodName,argsTypes);
    Object res = aMethod.invoke(process, methodArgs); // pass arg
    return(res);
}

et voici comment vous l'utilisez:

String className = "com.example.helloworld";
String methodName = "print";
Class<?>[] argsTypes = {String.class,  String.class};
Object[] methArgs = { "hello", "world" };   
launchProcess(className, methodName, argsTypes, methArgs);

0

Avec jooR, c'est simplement:

on(obj).call(methodName /*params*/).get()

Voici un exemple plus élaboré:

public class TestClass {

    public int add(int a, int b) { return a + b; }
    private int mul(int a, int b) { return a * b; }
    static int sub(int a, int b) { return a - b; }

}

import static org.joor.Reflect.*;

public class JoorTest {

    public static void main(String[] args) {
        int add = on(new TestClass()).call("add", 1, 2).get(); // public
        int mul = on(new TestClass()).call("mul", 3, 4).get(); // private
        int sub = on(TestClass.class).call("sub", 6, 5).get(); // static
        System.out.println(add + ", " + mul + ", " + sub);
    }
}

Cela imprime:

3, 12, 1


-10

pour moi, un moyen assez simple et à toute épreuve serait de simplement faire une méthode d'appel de méthode comme ceci:

public static object methodCaller(String methodName)
{
    if(methodName.equals("getName"))
        return className.getName();
}

puis quand vous devez appeler la méthode, mettez simplement quelque chose comme ça

//calling a toString method is unnessary here, but i use it to have my programs to both rigid and self-explanitory 
System.out.println(methodCaller(methodName).toString()); 

4
Si l'instance est déjà connue pendant la compilation, pourquoi ne le faites-vous pas simplement className.getName().toString()? Vous manquez tout le point de réflexion.
BalusC

Comme je l'ai dit, inutile dans ce cas, mais en supposant que vous saurez toujours que l'instance est une mauvaise habitude de programmation.
SMayne

2
@SMayne: Je suggère de supprimer ce message.
lpapp

une mauvaise programmation serait plutôt un compliment dans ce cas
pdenti
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.