Je suis nouveau sur java et j'aimerais savoir comment faire cela mieux en java ou plus simple, plus propre, plus facile, comme avoir plusieurs types de trucs génériques

public class Item<T1, T2, T3, T4, T5, T6, T7, T8, T9>{
    private T1 t1;
    private T2 t2;
    private T3 t3;
    private T4 t4;
    private T5 t5;
    private T6 t6;
    private T7 t7;
    private T8 t8;
    private T9 t9;

    public Item(T1 t1){
        this(t1, null, null, null, null, null, null, null, null);
    }
    public Item(T1 t1, T2 t2){
        this(t1, t2, null, null, null, null, null, null, null);
    }

    public Item(T1 t1, T2 t2, T3 t3){
        this(t1, t2, t3, null, null, null, null, null, null);
    }

    public Item(T1 t1, T2 t2, T3 t3, T4 t4){
        this(t1, t2, t3, t4, null, null, null, null, null);
    }
    ...
1
Ckkn 21 avril 2017 à 16:54

3 réponses

Meilleure réponse

Clause de non-responsabilité: (pour arrêter les votes contre ;-))

c'est juste une solution simple pour contenir un nombre arbitraire d'objets de type différent qui sont récupérables par un type réel d'une manière, encore une fois, simple sans garantie de sécurité de type. À utiliser avec prudence!

Solution proposée:

Vous pouvez simplement utiliser un simple List<Object>. Comme vous ne voulez pas préciser vos besoins réels, cela pourrait déjà vous suffire ... Voici un exemple:

public class Item {
  List<Object> items = new ArrayList<>();

  public <T> Item(T... items) { // well... could also just be Object
     Collections.addAll(this.items, items);
  }

  public <T> T get(int index) {
     return (T) items.get(index); // unsafe of course... but no requirement said something about type safety ;-)
  }

  public static void main(String[] args) {
    Item item = new Item("sum", 123L, 234L, true);

    if (item.get(3)) {
      long sum = item.<Long>get(1) + item.<Long>get(2);
      System.out.println(item.get(0) + " is " + sum);
    }
  }
}

Qui imprime:

sum is 357

En ce qui concerne la sécurité des types, vous pouvez améliorer cela un peu en fournissant un type lors de la récupération et échouer lamentablement si l'objet récupéré n'est pas du type attendu:

public class Item {
  List<Object> items = new ArrayList<>();

  public <T> Item(T... items) {
    Collections.addAll(this.items, items);
  }

  public <T> T get(int index, Class<T> type) {
    Object item = items.get(index);
    if (type.isInstance(item)) {
      return type.cast(item);
    }
    throw new RuntimeException("failing miserably as index " + index + " isn't of type " + type);
  }

  public static void main(String[] args) {
    Item item = new Item("sum", 123L, 234L, true);

    if (item.get(3, Boolean.class)) {
      long sum = item.get(1, Long.class) + item.get(2, Long.class);
      System.out.println(item.get(0, String.class) + " is " + sum);
    }
  }
}

Comme d'autres le suggèrent: un modèle de générateur peut également aider, mais dès que vous ajoutez plus de types ou que vous souhaitez en supprimer, vous devez adapter le code. Et avec le modèle de générateur, vous devez toujours écrire toutes ces informations de type générique si vous voulez déclarer une variable pour contenir votre item. Ce n'est pas nécessaire avec cette solution. Bien sûr, cette solution pose également quelques problèmes: la première variante n'est pas sûre de type et pourrait conduire à ClassCastException. La deuxième variante pourrait conduire à RuntimeException si vous voulez récupérer un objet qui n'a pas le type donné, mais au moins il est de type sûr sinon ;-) Cela dépend donc vraiment de ce que vous voulez accomplir.

Si vous n'aimez pas le RuntimeException dans la deuxième variante, vous pouvez aussi simplement utiliser un Optional à la place. J'ai omis cette variante délibérément car cela rend le code plus détaillé. Voici la mise en œuvre get pour cela:

public <T> Optional<T> get(int index, Class<T> type) {
  Object item = items.get(index);
  if (type.isInstance(item)) {
    return Optional.of(type.cast(item));
  }
  return Optional.empty();
}

Enfin, je n'utiliserais probablement pas ce code en production car il s'agit plutôt d'une solution de contournement pour épargner du code. Dans le code de production, je préfère la sécurité des types à une telle simplicité.

0
Roland 21 avril 2017 à 21:25

Vous pouvez utiliser des bibliothèques, des préprocesseurs de code ou le modèle de générateur, ou si vous souhaitez garder les choses simples, vous pouvez simplement créer des méthodes chaînables qui vous permettraient de définir uniquement les attributs que vous souhaitez, dans l'ordre de votre choix:

public class Item<T1, T2, T3, T4, T5, T6, T7, T8, T9> {
    private T1 t1;
    private T2 t2;
    private T3 t3;
    private T4 t4;
    private T5 t5;
    private T6 t6;
    private T7 t7;
    private T8 t8;
    private T9 t9;

    public Item<T1, T2, T3, T4, T5, T6, T7, T8, T9> t1(T1 t1) {
        this.t1 = t1;
        return this;
    }

    public Item<T1, T2, T3, T4, T5, T6, T7, T8, T9> t2(T2 t2) {
        this.t2 = t2;
        return this;
    }

    // TODO: rest of methods
}

Usage:

Item<String, Double, Integer, Long, Long, Long, Long, Long, String> item = new Item()
    .t1("hello")
    .t2(0.123)
    .t3(123)
    .t4(123L)
    .t5(123_456L)
    .t6(123_456_789L)
    .t7(654_321L)
    .t8(321L)
    .t9("goodbye");

Ou dans tout autre ordre:

Item<String, Double, Integer, Long, Long, Long, Long, Long, String> item = new Item()
    .t6(123_456_789L)
    .t2(0.123)
    .t3(123)
    .t5(123_456L)
    .t7(654_321L)
    .t8(321L)
    .t4(123L)
    .t1("hello")
    .t9("goodbye");

Ou juste avec quelques attributs:

Item<String, Double, Integer, Long, Long, Long, Long, Long, String> item = new Item()
    .t6(123_456_789L)
    .t2(0.123)
    .t3(123);
1
Federico Peralta Schaffner 21 avril 2017 à 19:05

Quelques suggestions qui pourraient vous être utiles:

  1. Vous avez un cas de l ' "anti-pattern de constructeur télescopique" . Le modèle de conception de constructeur est le remède habituel. Comme vous le mentionnez, vous recherchez une solution simple, je recommanderais d'utiliser les annotations lombok pour implémenter un générateur pour votre classe:

    @Getter
    @Builder
    class Item<T1, T2, T3, T4, T5, T6, T7, T8, T9> {
        private T1 t1;
        private T2 t2;
        private T3 t3;
        private T4 t4;
        private T5 t5;
        private T6 t6;
        private T7 t7;
        private T8 t8;
        private T9 t9;
    }
    

    Vous devrez initialiser un Item comme ceci:

    Item<String, Integer, Double, String, Long, Long, Long, Long, Long> item =
        Item.<String, Integer, Double, String, Long, Long, Long, Long, Long>builder()
                .t4("text").t2(42).t3(3.14159).build();
    

    Malheureusement, les informations de type sont dupliquées. Notez que vous pouvez ignorer les champs et initialiser dans n'importe quel ordre. Ce serait beaucoup de travail avec plusieurs constructeurs. Les autres champs par exemple t1, t5 sera laissé nul.

  2. Pensez à adopter Tuple9 de la bibliothèque jOOL. Si vous regardez le code source de Tuple9 ce n'est pas plus simple que votre code. La simplicité de cette approche est que quelqu'un d'autre a effectué la mise en œuvre à votre place. La bibliothèque prend en charge jusqu'à Tuple16

1
Manos Nikolaidis 21 avril 2017 à 15:25