2012-11-04 7 views
12

L'exécution du code ci-dessous entraîne l'affichage du message d'erreur Bad type on operand stack.Mauvais type sur la pile d'opérandes ... en utilisant jdk 8, lambda avec des classes internes anonymes échoue, pourquoi?

public static void main(String args[]) { 
     TransformService transformService = (inputs) -> { 
      return new ArrayList<String>(3) {{ 
       add("one"); 
       add("two"); 
       add("three"); 
      }}; 
     }; 

     Collection<Integer> inputs = new HashSet<Integer>(2) {{ 
      add(5); 
      add(7); 
     }}; 
     Collection<String> results = transformService.transform(inputs); 
     System.out.println(results.size()); 
    } 

    public interface TransformService { 
     Collection<String> transform(Collection<Integer> inputs); 
    } 

Cependant la suppression de la double initialisation de renfort (classes internes anonymes) dans le lamda permet le code de fonctionner comme prévu, pourquoi? Les travaux ci-dessous:

public class SecondLambda { 
    public static void main(String args[]) { 
     TransformService transformService = (inputs) -> { 
      Collection<String> results = new ArrayList<String>(3); 
      results.add("one"); 
      results.add("two"); 
      results.add("three"); 

      return results; 
     }; 

     Collection<Integer> inputs = new HashSet<Integer>(2) {{ 
      add(5); 
      add(7); 
     }}; 
     Collection<String> results = transformService.transform(inputs); 
     System.out.println(results.size()); 
    } 

    public interface TransformService { 
     Collection<String> transform(Collection<Integer> inputs); 
    } 
} 

Bogue de compilateur? Il est la première version d'accès après tout ...

(Ce ne compilera pas à moins que vous avez la dernière jdk 8 lambda download.)

+0

La balise lambda est destinée aux langages autres que Java comme C#, C++. – Lion

+8

@Lion: Je crois que Java 8 implémente lambdas (je ne l'ai jamais utilisé, donc je ne le sais pas pour un fait), et donc cette balise semble être appropriée à la question. –

+11

@Lion c'est seulement parce que Java n'a jamais eu de lambda auparavant. Un lambda est un lambda, même s'il s'agit d'une version alpha de la langue. –

Répondre

7

Il semble que ce problème se produit non seulement lorsque lambda renvoie le type anonymous, mais même si une classe anonyme est construite dans lambda. I.e. .:

public class TestLambda { 
    public static void main(String[] args) { 
     xxx(); 
    } 
    static void xxx() { 
     Functional1 f =() -> { 
      Object o = new Object() { }; 
      return new A(); 
     }; 
    } 
    static class A { } 
    static interface Functional1 { A func(); } 
} 

Cela conduit en fait Exception in thread "main" java.lang.VerifyError: Bad local variable type (...) Reason: Type top (current frame, locals[0]) is not assignable to reference type.

D'autres recherches montrent que si nous introduisons un paramètre dans la méthode xxx, la raison d'une exception contiendra son type. .: par exemple

Type 'java/lang/Integer' (current frame, stack[0]) is not assignable to 'lambda/TestLambda'

Et ce qui est déjà très intéressant. Changeons le type de classe supérieure, à savoir TestLambda type de paramètre xxx (qui est pas réellement utilisé):

... 
    xxx(new TestLambda()); 
} 
private static void xxx(TestLambda x) { 
... 

Et que pensez-vous? Cela résout le problème! Tout commence à bien fonctionner. Même, si nous allons changer return A(); à return new A() {};. Vérifie ça!


Ma conclusion est que ce qui est réel bug JVM . Il semble que le problème est avec la pile de classes chargées. Il se produit en combinaison avec la méthode, qui Java utilise pour traduire lambda expressions (http://cr.openjdk.java.net/~briangoetz/lambda/lambda-translation.html) - il produit des méthodes synthétiques à l'intérieur de classe supérieure. Il semble que lorsque des classes anonymes sont introduites dans lambda pile devient cassé. Il pourrait être réparé en utilisant la solution de contournement mentionnée.

2

bug du compilateur? Il est la première version d'accès après tout ...

je dirais que tout message d'erreur qui mentionne la pile d'opérandes est très probable d'être dû à un bug du compilateur ou un bogue dans la machine virtuelle Java. Surtout si vous pouvez l'obtenir en utilisant un exemple Java pur.

(Il ressemble à la machine virtuelle Java signale un problème de Sûreté du typage qui aurait dû être détectée par le compilateur, et/ou le vérificateur de bytecode au moment de la classe charge.)

rapport via le canal recommandé pour Java 8 bogues.

1

Non directement lié à votre problème, mais je recommanderais fortement de ne pas utiliser les classes anonymes de cette façon. Vous créez un sous-type HashSet entièrement nouveau uniquement dans le but d'y ajouter deux valeurs. Non seulement cela gonfle le système (il reste dans la mémoire pour toujours), mais il peut aussi confondre le JIT de la JVM puisqu'il ne voit jamais HashSet sur un site d'appel ... il voit l'un des nombreux sous-types que vous avez créés.

Questions connexes