In Java 5 wurden Parameter variabler Länge eingeführt, die es ermöglichen, beim Aufruf einer Methode eine unbestimmte Anzahl von Parametern zu übergeben. Parameter variabler Länge sind in Java syntaktischer Zucker und basieren im Wesentlichen auf einer Array-Implementierung:

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

Methoden definieren

Beim Definieren einer Methode fügt man nach dem letzten formalen Parameter…drei Punkte hinzu, was anzeigt, dass dieser formale Parameter mehrere Parameterwerte akzeptieren kann, und die mehreren Parameterwerte werden als Array übergeben. Bei der obigen Definition sind mehrere wichtige Punkte zu beachten:

  • Variable Parameter können nur der letzte Parameter einer Funktion sein, aber es können davor weitere Parameter stehen oder auch nicht.

  • Da variable Parameter der letzte Parameter sein müssen, kann eine Funktion höchstens einen variablen Parameter haben.

  • Java variable Parameter werden vom Compiler in ein Array umgewandelt.

  • Nachdem Parameter variabler Länge in Bytecode kompiliert wurden, erscheinen sie in der Methodensignatur in Array-Form. Die Signaturen dieser beiden Methoden sind identisch und können nicht als Methodenüberladung verwendet werden. Wenn sie gleichzeitig auftreten, kann der Code nicht kompiliert werden. Variable Parameter sind mit Arrays kompatibel, aber das Umgekehrte gilt nicht.

    public void foo(String...varargs){}
    
    foo("arg1", "arg2", "arg3");
    
    //上述过程和下面的调用是等价的
    foo(new String[]{"arg1", "arg2", "arg3"});
  • In J2SE 1.5 wurde der"Generics"-Mechanismus hinzugefügt, der einen Typ unter bestimmten Bedingungen parametrisieren kann. Wenn Sie beispielsweise eine Klasse schreiben, können Sie einen Bezeichner (wie T) verwenden, um den Typ eines formalen Parameters einer Methode darzustellen. Welchen Typ dieser Bezeichner tatsächlich repräsentiert, wird beim Erzeugen einer Instanz der Klasse festgelegt. Dieser Mechanismus kann eine bessere Code-Wiederverwendung und eine strengere Typüberprüfung zur Kompilierungszeit bieten. Allerdings kann der Generics-Mechanismus nicht zusammen mit formalen Parametern variabler Länge verwendet werden. Wenn der Typ eines formalen Parameters, der mit einer unbestimmten Anzahl tatsächlicher Argumente übereinstimmen kann, durch einen Bezeichner dargestellt wird, gibt der Compiler einen"generic array creation"Fehler.

    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){}
    }

Aufruf von Methoden mit variablen Argumenten

Beim Aufruf einer Methode mit variablen Argumenten können Sie null bis beliebig viele Argumente übergeben, und der Compiler konvertiert die variablen Argumente in ein Array. Sie können auch direkt ein Array übergeben, wie im folgenden Beispiel gezeigt:

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"});//直接传递数组
    }
}

Methodenüberladung

Feste Parameter werden zuerst abgeglichen

Beim Aufruf einer überladenen Methode wird, wenn der Aufruf sowohl mit einer überladenen Methode mit festen Parametern als auch mit einer überladenen Methode mit Parametern variabler Länge übereinstimmen kann, die Methode mit festen Parametern ausgewählt:

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
    }
}

> Abgleich mehrerer variabler Argumente

Beim Aufruf einer überladenen Methode tritt, wenn der Aufruf mit zwei überladenen Methoden mit Parametern variabler Länge übereinstimmen kann, ein Kompilierungsfehler auf:

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
    }
}

Methodenüberschreibung

Vermeiden Sie das Überladen von Methoden mit Parametern variabler Länge

Obwohl der Compiler die konkret aufzurufende Methode bestimmen kann, indem er feste Parameter bevorzugt, können Leser des Codes trotzdem leicht in Fallen tappen. Das Überladen von Methoden mit Parametern variabler Länge erfordert sorgfältige Überlegung.

Lassen Sie nicht zu, dass Nullwerte und leere Werte Methoden mit variabler Länge bedrohen

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
     }  
}

Ändern Sie es wie folgt:

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

Wenn man dem Compiler mitteilt, dass dieser Nullwert vom Typ String ist, kann die Kompilierung reibungslos durchlaufen, wodurch das Auftreten von Fehlern verringert wird.

Das Überschreiben von Methoden mit variabler Länge muss ebenfalls den Regeln folgen

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");
    }
}

Das erste lässt sich erfolgreich kompilieren. Warum ist das so? Tatsächlich führt das Basisobjekt einen Upcast für das Unterklassenobjekt sub durch, und die Parameterliste wird von der Elternklasse bestimmt, daher passt es natürlich. Nun betrachten wir den Fall des direkten Aufrufs durch die Unterklasse. Der Compiler sieht, dass die Unterklasse die print-Methode der Elternklasse überschrieben hat, also wird er definitiv die von der Unterklasse neu definierte print-Methode verwenden. Selbst wenn die Parameterliste nicht übereinstimmt, wird er nicht zur Elternklasse gehen, um erneut abzugleichen, denn sobald er eine Übereinstimmung findet, hört er auf zu suchen. Daher der Fehler wegen Typinkompatibilität.

Dies ist ein Sonderfall. Die Parameterliste einer überschreibenden Methode kann tatsächlich von der der Elternklasse abweichen, was der Definition des Überschreibens widerspricht und zu unerklärlichen Fehlern führen kann.

Fassen wir hier die Bedingungen zusammen, die das Überschreiben erfüllen muss:

  • Eine überschreibende Methode darf die Zugriffsberechtigungen nicht einschränken.

  • Die Parameterliste muss mit der überschriebenen Methode übereinstimmen (einschließlich der expliziten Form).

  • Der Rückgabetyp muss derselbe sein wie der der überschriebenen Methode oder eine Unterklasse davon.

  • Eine überschreibende Methode darf keine neuen checked Exceptions werfen oder Exceptions, die außerhalb des Geltungsbereichs der Elternklasse liegen, aber sie darf weniger, eingeschränktere Exceptions oder gar keine Exceptions werfen.


Mögliche Probleme

Verwendung vonObject…als Parameter variabler Länge:

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[]kann nicht inObject[]umgewandelt werden und wird daher als einfaches Array-Objekt behandelt;Integer[]kann inObject[]umgewandelt werden und kann als Objekt-Array verwendet werden.


Vorsichtsmaßnahmen beim Aufruf von Methoden per Reflexion

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();
        }
    }
}

Von den vier obigen Aufrufen lösen die ersten beiden eine Laufzeitausnahmejava.lang.IllegalArgumentException: wrong number of argumentsaus, während die letzten beiden normal aufgerufen werden.

Reflexion wird zur Laufzeit ermittelt. Aus Laufzeitsicht sind Parameter variabler Länge und Arrays identisch, daher lautet die Methodensignatur:

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

Schauen wir uns auch die Methodendeklaration des Method-Objekts an:

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

Obwohl args ein Parameter variabler Länge ist, ist seine Länge durch die Länge der Parameterliste der tatsächlichen Methode begrenzt, die durch das Method-Objekt repräsentiert wird. Von der Laufzeitsignatur her hat ([Ljava/lang/String;)V tatsächlich nur einen formalen Parameter, nämlich String[] varargs. Daher kann in invoke(Object obj, Object… args) die tatsächliche Argumentlänge des variablen Parameters args nur 1 sein.

//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,正确。上一个和这个是等价的

Wann sind Argumente variabler Länge zu verwenden?

Stack OverflowEs gibt eine Frage zur Verwendung von Parametern variabler Länge. Kurz gesagt,
Sie können Parameter variabler Länge verwenden, wenn Sie sich nicht sicher sind, wie viele Objekte die Methode verarbeiten muss. Dies macht Methodenaufrufe einfacher, ohne dass Arrays manuell erstellt werden müssen.new T[]{…}

Originaladresse: https://blog.csdn.net/qiuchengjia/article/details/52910888