web-dev-qa-db-fra.com

Java deux variables dans une méthode

Existe-t-il un moyen en Java de créer une méthode qui attend deux variables différentes? Mais pourquoi est-ce également impossible avec différents types d'objets?

Par exemple: 

public void doSomething(String... s, int... i){
    //...
    //...
}

Y a-t-il un moyen de créer une méthode comme celle-ci? Merci!

27
T_01

Un seul vararg, désolé. Mais utiliser asList () le rend presque aussi pratique:

 public void myMethod(List<Integer> args1, List<Integer> args2) {
   ...
 }

 -----------

 import static Java.util.Arrays.asList;
 myMethod(asList(1,2,3), asList(4,5,6));
32
rec

En Java, un seul argument varargs est autorisé et il doit s'agir du dernier paramètre de la signature.

Mais tout ce que ça fait, c'est le convertir en tableau de toute façon, vous devriez donc simplement rendre explicites vos tableaux de tableaux:

public void doSomething(String[] s, int[] i){
17
rgettman

Une possible conception d'API dans laquelle le code d'appel ressemble

    doSomething("a", "b").with(1,2);

via une API "fluide"

public Intermediary doSomething(String... strings)
{
    return new Intermediary(strings);
}

class Intermediary
{
    ...
    public void with(int... ints)
    {
        reallyDoSomething(strings, ints);
    }
}

void reallyDoSomething(String[] strings, int[] ints)
{
    ...
}

Le danger est que le programmeur oublie d'appeler with(...)

    doSomething("a", "b");  // nothing is done

Peut-être que c'est un peu mieux

    with("a", "b").and(1, 2).doSomething();
9
ZhongYu

Bien que ce genre de chose soit parfois utile, habituellement, si vous constatez que vous respectez une restriction en Java, vous pourrez probablement redéfinir quelque chose et en sortir beaucoup mieux. Voici quelques autres façons possibles de le regarder ...

Si les deux listes sont liées, vous souhaiterez probablement créer une classe wrapper pour les deux listes différentes et transmettre le wrapper. Les wrappers autour des collections sont presque toujours une bonne idée - ils vous permettent d'ajouter du code lié à la collection.

S'il s'agit d'un moyen d'initialiser des données, analysez-les à partir d'une chaîne. Par exemple, "abc, 123: def, 456: jhi, 789" est extrêmement facile à scinder avec 2 instructions divisées et une boucle (2-3 lignes de code). Vous pouvez même créer une petite classe d’analyseur personnalisée qui analyse une telle chaîne dans une structure que vous introduisez dans votre méthode.

Hmm - honnêtement, aide à l'initialisation des données. Je ne sais même pas pourquoi vous voudriez le faire de toute façon, dans aucun autre cas et je m'attends à ce que vous transmettiez deux collections et que les varags ne vous intéressent pas du tout.

4
Bill K

Un seul vararg est autorisé. En effet, plusieurs arguments vararg sont ambigus. Par exemple, si vous passiez dans deux varargs de la même classe?

public void doSomething(String...args1, String...args2);

Où finit args1 et où commence args2? Ou que diriez-vous de quelque chose de plus déroutant ici.

class SuperClass{}
class ChildClass extends SuperClass{}
public void doSomething(SuperClass...args1, ChildClass...args2);

ChildClass étend SuperClass et peut donc exister légalement dans args1 ou args2. Cette confusion est la raison pour laquelle une seule varargs est autorisée.

varargs doit également apparaître à la fin d'une déclaration de méthode.

Déclarez simplement le type spécifique en tant que 2 tableaux.

4
William Morrison

Vous pouvez faire quelque chose comme ceci, puis vous pouvez lancer et ajouter une logique supplémentaire dans cette méthode.

public void doSomething(Object... stringOrIntValues) {
    ...
    ...
}

Et utilisez cette méthode comme suit:

doSomething(stringValue1, stringValue2, intValue1, intValue2,         
    intValue3);
2
LEMUEL ADANE

C'est un vieux fil, mais je pensais que cela serait utile malgré tout. 

La solution que j'ai trouvée n'est pas très élégante mais elle fonctionne. J'ai créé une classe séparée pour gérer le gros du travail. Il ne contient que les deux variables dont j'avais besoin et leurs accesseurs. Le constructeur gère lui-même les méthodes set.

Je devais passer des objets de direction et un objet de données respectif. Cela résout également le problème potentiel de paires de données inégales, mais ce n'est probablement que pour mes besoins d'utilisation.

public class DataDirectionPair{

    Data dat;
    Directions dir;

    public DataDirectionPair(Data dat, Directions dir) {
        super();
        this.dat = dat;
        this.dir = dir;
    }

    /**
     * @return the node
     */
    public Node getNode() {
        return node;
    }

    /**
     * @return the direction
     */
    public Directions getDir() {
        return dir;
    }
}

Je passerais alors juste cette classe comme vararg pour la méthode

public void method(DataDirectionPair... ndPair){
    for(DataDirectionPair temp : ndPair){
        this.node = temp.getNode();
        this.direction = temp.getDir();
        //or use it however you want
    }
}
1
user3709234

Ce n'est pas possible car la spécification du langage Java le dit (voir 8.4.1. Paramètres de forme ):

Le dernier paramètre formel d'une méthode ou d'un constructeur est spécial: it peut être un paramètre variable d'arity, indiqué par un Ellipsis suivant le type.

Notez que l'ellipsis (...) est un gage en soi (§3.11). Il est possible de mettre des espaces entre lui et le type, mais c'est découragé pour des raisons de style.

Si le dernier paramètre formel est un paramètre d'arity variable, la méthode est une méthode variable arity. Sinon, c'est une méthode fixed arity.

Pour ce qui est de savoir pourquoi un seul et unique dernier paramètre, ce serait une supposition, mais probablement parce que permettre cela pourrait conduire à des problèmes indécidables ou ambigus (par exemple, considérez ce qui se passe avec method(String... strings, Object... objects)), et le fait de ne laisser que des types ne se croisant pas entraînerait des complications ( par exemple, en considérant des refactorisations où se trouvent soudainement des types précédemment non intersectés), un manque de clarté lorsque cela fonctionne ou non, et une complexité pour le compilateur de décider quand cela est applicable ou non.

1
Mark Rotteveel

Si vous ne passez pas un grand nombre de chaînes la plupart du temps pour le premier argument, vous pouvez créer un ensemble de surcharges prenant un nombre différent de chaînes et les envelopper dans un tableau avant d'appeler une méthode premier argument.

public void doSomething(int... i){
    doSomething(new String[0], i);
}
public void doSomething(String s, int... i){
    doSomething(new String[]{ s }, i);
}
public void doSomething(String s1, String s2, int... i){
    doSomething(new String[]{ s1, s2 }, i);
}
public void doSomething(String s1, String s2, String s3, int... i){
    doSomething(new String[]{ s1, s2, s3 }, i);
}
public void doSomething(String[] s, int... i) {
    // ...
    // ...
}
0
Alex

Vous pouvez convertir vos varargs en tableaux

public void doSomething(String[] s, int[] i) {
    ...
}

puis avec quelques méthodes d'assistance pour convertir vos varargs en tableau comme ceci:

public static int[] intsAsArray(int... ints) {
    return ints;
}

public static <T> T[] asArray(T... ts) {
    return ts;
}

Vous pouvez ensuite utiliser ces méthodes d'assistance pour convertir vos paramètres vararged.

doSomething(asArray("a", "b", "c", "d"), intsAsArray(1, 2, 3));
0
Mahpooya

Je viens de lire une autre question à propos de ce "modèle", mais il a déjà été supprimé. J'aimerais donc proposer une approche différente de ce problème, car je n'ai pas vu ici cette solution.

Au lieu de forcer le développeur à encapsuler le paramètre input dans List ou Array, il sera utile d'utiliser une approche "curry" ou d'améliorer le modèle de générateur.

Considérons le code suivant:

/**
 * Just a trivial implementation
 */
public class JavaWithCurry {

    private List<Integer> numbers = new ArrayList<Integer>();
    private List<String> strings = new ArrayList<String>();

    public JavaWithCurry doSomething(int n) {

        numbers.add(n);

        return this;
    }

    public JavaWithCurry doSomething(String s) {

        strings.add(s);

        return this;

    }

    public void result() {

        int sum = -1;

        for (int n : numbers) {
            sum += n;
        }


        StringBuilder out = new StringBuilder();

        for (String s : strings) {

            out.append(s).append(" ");

        }

        System.out.println(out.toString() + sum);

    }

    public static void main(String[] args) {

        JavaWithCurry jwc = new JavaWithCurry();

        jwc.doSomething(1)
                .doSomething(2)
                .doSomething(3)
                .doSomething(4)
                .doSomething(5)
                .doSomething("a")
                .doSomething("b")
                .doSomething("c")
                .result();

    }

}

Comme vous pouvez le voir de cette manière, vous pouvez ajouter de nouveaux éléments du type dont vous avez besoin quand vous en avez besoin.

Toute l'implémentation est emballée.

0
Mario Santini

suivi de Lemuel Adane (je ne peux pas commenter le post, faute de représentant :))

si tu utilises 

public void f(Object... args){}

alors vous pouvez boucler en utilisant Comment déterminer la classe d'un objet (en Java)?

comme par exemple

{
   int i = 0;
   while(i< args.length && args[i] instanceof String){
         System.out.println((String) args[i]);
         i++ ;
   }
   int sum = 0;
   while(i< args.length){
         sum += (int) args[i];
         i++ ;
   }
   System.out.println(sum);
}

ou tout ce que vous avez l'intention de faire.

0
user3617487