Question

class Bouncy<T> extends Throwable {     
}
// Error: the generic class Bouncy<T> may not subclass java.lang.Throwable

Pourquoi ne supporte pas Java Throwables génériques?

Je me rends compte que l'effacement de type complique certaines choses, mais évidemment Java se débrouille avec déjà beaucoup, alors pourquoi pas pousser d'un cran et laissez Throwables génériques, avec vérification de la compilation complète des problèmes potentiels?


Je me sens comme l'argument d'effacement de type est plutôt faible. À l'heure actuelle, nous ne pouvons pas faire:

void process(List<String> list) {
}

void process(List<Integer> list) {
}

Bien sûr, nous vous en passer. Je ne demande pas que nous devrions être en mesure de le faire catch Bouncy<T1> et Bouncy<T2> dans le même bloc de try, mais si nous les utilisons dans des contextes disjoints avec stricte de compilation des règles exécutoires (ce qui est à peu près les génériques de la manière dont fonctionne en ce moment), wouldn « t ce soit réalisable?

Était-ce utile?

La solution

Java Language Specification 8.1.2 Classes génériques et Paramètres de type :

  

Cette restriction est nécessaire car le mécanisme de capture de la machine virtuelle Java fonctionne uniquement avec les classes non génériques.

Personnellement, je pense qu'il est parce que nous ne pouvons pas obtenir des avantages de médicaments génériques dans une clause de catch. Nous ne pouvons pas écrire catch (Bouncy<String> ex) en raison de l'effacement de type, mais si nous écrivons catch (Bouncy ex), il serait inutile de le rendre générique.

Autres conseils

effacement de type. Runtime type d'exception n'a pas d'informations génériques. Ainsi vous ne peut pas faire

} catch( Mistake<Account> ea) {
  ...
} catch( Mistake<User> eu) {
...
}

peut faire est

catch( Mistake ea ) {
  ...
}

Et l'effacement de type est la façon dont il a été décidé de préserver la compatibilité descendante lorsque Java se déplaçait de 1,4 à 1,5. Beaucoup de gens étaient malheureux alors, et à juste titre. Mais ayant à l'esprit la quantité de code déployé, il était impensable de casser le code qui a fonctionné avec bonheur dans 1.4.

Réponse courte:. Parce qu'ils ont pris des raccourcis, comme ils l'ont fait avec l'effacement

Réponse longue: comme d'autres déjà indiqué, en raison de l'effacement, il n'y a pas moyen de faire une différence lors de l'exécution entre un "catch MyException " et "attraper MyException "

.

Mais qui ne veut pas dire qu'il n'y a pas besoin d'exceptions génériques . Je veux des médicaments génériques pour être en mesure d'utiliser des champs génériques! Ils auraient pu simplement permis des exceptions génériques, mais ne permettent de les attraper à l'état brut (par exemple « attraper MyException »).

Certes, cela rendrait génériques encore plus compliquée. Ceci est de montrer à quel point la décision d'effacer les génériques était. Quand aurons-nous une version Java qui prend en charge les génériques réels (avec RTTI), pas le sucre syntaxique actuel?

Vous pouvez toujours utiliser des méthodes génériques, comme ceci:

public class SomeException {
    private final Object target;

    public SomeException(Object target) {
        this.target = target;
    }

    public <T> T getTarget() {
        return (T) target;
    }
}

....

catch (SomeException e) {
    Integer target = e.getTarget();
}

Je suis d'accord avec la réponse de Cristian ci-dessus. Bien que la réponse acceptée est techniquement correcte (dans la mesure où il fait référence aux spécifications JVM), la réponse de Cristian Vasile est celui qui se qualifie et remet en question même la limitation.

Il y a moins deux arguments que je l'ai noté dans les réponses à cette question que je ne suis pas d'accord avec et que je réfuter. Si les arguments de ces réponses sont correctes, nous pourrions utiliser ces arguments pour attaquer génériques dans d'autres contextes où ils sont utilisés avec succès aujourd'hui.


Les premiers états de l'argument que nous ne pouvons pas utiliser ceci:

catch (Exception<T1> e) {}

parce que la machine virtuelle Java ne sait pas comment travailler avec Exception<T1>. Cet argument semble attaquer également cette utilisation des médicaments génériques, sur la base que la machine virtuelle Java ne sait pas comment utiliser List<T1>:

List<T1> list;

L'argument, bien sûr, oublie que le compilateur effectue ce type d'effacement et de sorte que la machine virtuelle Java n'a pas besoin de savoir comment gérer Exception<T1>. Il peut simplement gérer Exception, comme il gère List.

Bien sûr, nous ne pourrions jamais gérer catch(Exception<T1> e) et catch(Exception<T2> e) dans le même try / catch en raison de l'effacement de type, mais là encore, ce n'est pas pire que les arguments de la méthode ou les valeurs de retour aujourd'hui: nous ne traitons aujourd'hui myMethod(List<T1>) et myMethod(List<T2>) soit ... (Je réitère cet aspect dans la deuxième réfutation ci-dessous.)


Un second argument est le suivant. Nous ne permettons pas:

catch (Exception<T1> e) {}

parce que cela ne fonctionnerait pas:

catch (Exception<T1> e) {}
catch (Exception<T2> e) {}

OK, alors pourquoi ne pas désavouer ceci:

interface MyInterface {
    Comparable<Integer> getComparable();
}

parce que ce ne fonctionne pas :

interface MyInterface {
    Comparable<Integer> getComparable();
    Comparable<String> getComparable();
}

ou ceci:

interface MyInterface {
    void setComparable(Comparable<Integer> comparable);
}

parce que ce ne fonctionne pas :

interface MyInterface {
    void setComparable(Comparable<Integer> comparable);
    void setComparable(Comparable<String> comparable);
}

En d'autres termes, pourquoi ne pas interdire les médicaments génériques dans la plupart des cas?

Ce second argument oublie que, même si nous ne pouvions pas permettre à différentes constructions génériques qui effacent à la même construction non générique dans ces contextes, nous pouvons encore faire la meilleure chose et permettent génériques aussi longtemps que les types n'effacent pas au même type . C'est ce que nous faisons avec les paramètres de la méthode: nous vous permettons d'utiliser les médicaments génériques, mais plaignez dès que nous détectons les signatures en double après l'effacement de type. Eh bien, nous aurions pu faire la même chose avec des exceptions et des blocs catch ...


En conclusion, je voudrais développer la réponse de Cristian. Au lieu de laisser les classes d'exception générique et en utilisant les types bruts dans des blocs de catch:

class MyException<T> {}
...
catch (MyException e) { // raw

Java aurait pu aller tout le chemin sans problème:

class MyException<T> {}
...
catch (MyException<Foo> e) {

Voici quelques choses que vous peut faire:

  1. Throwables peut implémenter des interfaces génériques, tant que le throwable lui-même n'a pas de paramètres de type, par exemple

      

    interface Bouncy<E> {
          // ...
      }
      class BouncyString extends Exception implements Bouncy<String> {
          // ...
      }

  2.   
  3. Une clause de throws peut se référer à des paramètres de type, par exemple
      static <X extends Throwable> void
      throwIfInstanceOf(Throwable ex, Class<X> clazz) throws X {
          if (clazz.isInstance(ex)) throw clazz.cast(ex);
      }   
  4.   

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top