Dans Java 5, les paramètres de longueur variable ont été introduits, permettant de passer un nombre indéfini de paramètres lors de l'appel d'une méthode. Les paramètres de longueur variable sont du sucre syntaxique en Java, essentiellement basés sur une implémentation par tableau :

void foo(String... args);
void foo(String[] args);
//方法签名 
([Ljava/lang/String;)V // public void foo(String[] args)

Définition des méthodes

Lors de la définition d'une méthode, ajoutez trois points après le dernier paramètre formel…, ce qui indique que ce paramètre formel peut accepter plusieurs valeurs de paramètre, et ces multiples valeurs de paramètre sont transmises sous forme de tableau. Il y a plusieurs points clés à noter dans la définition ci-dessus :

  • Les paramètres variables ne peuvent être que le dernier paramètre d'une fonction, mais il peut y avoir ou non d'autres paramètres avant eux.

  • Comme les paramètres variables doivent être le dernier paramètre, une fonction peut avoir au plus un paramètre variable.

  • Les paramètres variables Java sont transformés par le compilateur en un tableau.

  • Après la compilation des paramètres de longueur variable en bytecode, ils apparaissent dans la signature de méthode sous forme de tableau. Les signatures de ces deux méthodes sont identiques et ne peuvent pas être utilisées comme surcharge de méthode. Si elles apparaissent en même temps, le code ne peut pas être compilé. Les paramètres variables sont compatibles avec les tableaux, mais l'inverse n'est pas vrai.

    public void foo(String...varargs){}
    
    foo("arg1", "arg2", "arg3");
    
    //上述过程和下面的调用是等价的
    foo(new String[]{"arg1", "arg2", "arg3"});
  • Dans J2SE 1.5, le mécanisme"génériques"a été ajouté, qui peut paramétrer un type sous certaines conditions. Par exemple, lors de l'écriture d'une classe, vous pouvez utiliser un identificateur (tel que T) pour représenter le type d'un paramètre formel de méthode. Le type que cet identificateur représente réellement est spécifié lors de la génération d'une instance de la classe. Ce mécanisme peut fournir une réutilisation de code plus suffisante et une vérification de type à la compilation plus stricte. Cependant, le mécanisme des génériques ne peut pas être utilisé conjointement avec des paramètres formels de longueur variable. Si le type d'un paramètre formel pouvant correspondre à un nombre indéfini d'arguments réels est représenté par un identificateur, le compilateur donnera une"création de tableau générique"erreur.

    public class Varargs {
    
        public static void test(String... args) {
            for(String arg : args) {//当作数组用foreach遍历
                System.out.println(arg);
            }
        }
        //Compile error
        //The variable argument type Object of the method must be the last parameter
        //public void error1(String... args, Object o) {}
        //public void error2(String... args, Integer... i) {}
    
        //Compile error
        //Duplicate method test(String...) in type Varargs
        //public void test(String[] args){}
    }

Appel de méthodes à arguments variables

Lors de l'appel d'une méthode à arguments variables, vous pouvez passer de zéro à un nombre quelconque d'arguments, et le compilateur convertira les arguments variables en un tableau. Vous pouvez également passer directement un tableau, comme le montre l'exemple suivant :

public class Varargs {

    public static void test(String... args) {
        for(String arg : args) {
            System.out.println(arg);
        }
    }

    public static void main(String[] args) {
        test();//0个参数
        test("a");//1个参数
        test("a","b");//多个参数
        test(new String[] {"a", "b", "c"});//直接传递数组
    }
}

Surcharge de méthode

Les paramètres fixes sont mis en correspondance en premier

Lors de l'appel d'une méthode surchargée, si l'appel peut correspondre à la fois à une méthode surchargée avec des paramètres fixes et à une méthode surchargée avec des paramètres de longueur variable, la méthode à paramètres fixes est sélectionnée :

public class Varargs {

    public static void test(String... args) {
        System.out.println("version 1");
    }

    public static void test(String arg1, String arg2) {
        System.out.println("version 2");
    }
    public static void main(String[] args) {
        test("a","b");//version 2 优先匹配固定参数的重载方法
                test();//version 1
    }
}

> Mise en correspondance de plusieurs arguments variables

Lors de l'appel d'une méthode surchargée, si l'appel peut correspondre à deux méthodes surchargées avec des paramètres de longueur variable, une erreur de compilation se produit :

public class Varargs {

    public static void test(String... args) {
        System.out.println("version 1");
    }

    public static void test(String arg1, String... arg2) {
        System.out.println("version 2");
    }
    public static void main(String[] args) {
        test("a","b");//Compile error
    }
}

Redéfinition de méthode

Éviter la surcharge des méthodes avec des paramètres de longueur variable

Bien que le compilateur puisse déterminer la méthode spécifique à appeler en privilégiant les paramètres fixes, les lecteurs du code peuvent encore facilement tomber dans des pièges. Surcharger des méthodes avec des paramètres de longueur variable exige une réflexion attentive.

Ne laissez pas les valeurs null et les valeurs vides menacer les méthodes à paramètres variables

public class Client {  
     public void methodA(String str,Integer... is){       
     }  

     public void methodA(String str,String... strs){          
     }  

     public static void main(String[] args) {  
           Client client = new Client();  
           client.methodA("China", 0);  
           client.methodA("China", "People");  
           client.methodA("China");  //compile error
           client.methodA("China",null);  //compile error
     }  
}

Modifiez-le comme suit :

public static void main(String[] args) {  
     Client client = new Client();  
     String[] strs = null;  
     client.methodA("China",strs);  
}

Faire savoir au compilateur que cette valeur null est de type String permet à la compilation de se dérouler sans problème, ce qui réduit l'apparition d'erreurs.

Redéfinir des méthodes à paramètres variables doit aussi respecter les règles

package com;
public class VarArgsTest2 {
    /**
     * @param args
     */
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        // 向上转型
        Base base = new Sub();
        base.print("hello");
        // 不转型
        Sub sub = new Sub();
        sub.print("hello");//compile error
    }
}
// 基类
class Base {
    void print(String... args) {
        System.out.println("Base......test");
    }
}
// 子类,覆写父类方法
class Sub extends Base {
    @Override
    void print(String[] args) {
        System.out.println("Sub......test");
    }
}

Le premier peut être compilé avec succès. Pourquoi cela ? En fait, l'objet base effectue un upcast de l'objet de sous-classe sub, et la liste de paramètres est déterminée par la classe parente, donc cela passe naturellement. Regardons maintenant le cas de l'appel direct par la sous-classe. Le compilateur voit que la sous-classe a redéfini la méthode print de la classe parente, il utilisera donc certainement la méthode print redéfinie par la sous-classe. Même si la liste de paramètres ne correspond pas, il ne retournera pas vers la classe parente pour une nouvelle correspondance, car dès qu'il trouve une correspondance, il arrête la recherche. D'où l'erreur d'incompatibilité de types.

Ceci est un cas particulier. La liste de paramètres d'une méthode de redéfinition peut en fait être différente de celle de la classe parente, ce qui viole la définition de la redéfinition et peut provoquer des erreurs inexplicables.

Ici, résumons les conditions que la redéfinition doit remplir :

  • Une méthode de redéfinition ne peut pas réduire les permissions d'accès.

  • La liste de paramètres doit être identique à celle de la méthode redéfinie (y compris la forme explicite).

  • Le type de retour doit être identique à celui de la méthode redéfinie, ou être une sous-classe de celui-ci.

  • Une méthode de redéfinition ne peut pas lever de nouvelles exceptions vérifiées, ni d'exceptions au-delà de la portée de la classe parente, mais elle peut lever moins d'exceptions, des exceptions plus limitées, ou aucune exception.


Problèmes potentiels

Utilisation d'Object…comme paramètre de longueur variable :

public void foo(Object... args) {
    System.out.println(args.length);
}

foo(new String[]{"arg1", "arg2", "arg3"}); //3
foo(100, new String[]{"arg1", "arg1"}); //2

foo(new Integer[]{1, 2, 3}); //3
foo(100, new Integer[]{1, 2, 3}); //2
foo(1, 2, 3); //3
foo(new int[]{1, 2, 3}); //1

int[]ne peut pas être converti enObject[], et est donc traité comme un objet tableau ordinaire ;Integer[]peut être converti enObject[], et peut être utilisé comme un tableau d'objets.


Précautions lors de l'appel de méthodes par réflexion

public class Test {
    public static void foo(String... varargs){
        System.out.println(args.length);
    }

    public static void main(String[] args){
        String[] varArgs = new String[]{"arg1", "arg2"};
        try{
            Method method = Test.class.getMethod("foo", String[].class);
            method.invoke(null, varArgs);
            method.invoke(null, (Object[])varArgs);
            method.invoke(null, (Object)varArgs);
            method.invoke(null, new Object[]{varArgs});
        } catch (Exception e){
            e.printStackTrace();
        }
    }
}

Parmi les quatre appels ci-dessus, les deux premiers lèveront une exception d'exécutionjava.lang.IllegalArgumentException: wrong number of arguments, tandis que les deux derniers sont appelés normalement.

La réflexion est obtenue à l'exécution. Du point de vue de l'exécution, les paramètres de longueur variable et les tableaux sont identiques, donc la signature de méthode est :

//方法签名
([Ljava/lang/String;)V // public void foo(String[] varargs)

Regardons aussi la déclaration de méthode de l'objet Method :

Object invoke(Object obj, Object... args)

Bien que args soit un paramètre de longueur variable, sa longueur est limitée par la longueur de la liste de paramètres de la méthode réelle représentée par l'objet Method. D'après la signature d'exécution, ([Ljava/lang/String;)V n'a en fait qu'un seul paramètre formel, à savoir String[] varargs. Par conséquent, dans invoke(Object obj, Object… args), la longueur effective des arguments du paramètre variable args ne peut être que 1.

//Object invoke(Object obj, Object... args)
//String[] varArgs = new String[]{"arg1", "arg2"};
method.invoke(null, varArgs); //varArgs长度为2,错误
method.invoke(null, (Object[])varArgs); //将String[]转换为Object[],长度为2的,错误
method.invoke(null, (Object)varArgs);//将整个String[] 转为Object,长度为1,符合
method.invoke(null, new Object[]{varArgs});//Object[]长度为1,正确。上一个和这个是等价的

Quand utiliser des arguments de longueur variable ?

Stack OverflowIl y a une question sur l'utilisation de paramètres de longueur variable. En bref,
Vous pouvez utiliser des paramètres de longueur variable lorsque vous n'êtes pas sûr du nombre d'objets que la méthode doit traiter. Cela simplifie les appels de méthode, sans avoir besoin de créer manuellement des tableaux.new T[]{…}

Adresse d'origine : https://blog.csdn.net/qiuchengjia/article/details/52910888