2017-05-17 1 views
1

J'observe un comportement très étrange de référence de méthode. Nous avons une application Web et une chaîne de hiérarchie de servlet: Servlet A étend le servlet B étend HttpServlet (ignorons pourquoi avons-nous besoin de cela).La référence de méthode d'instance Java 8 est-elle équivalente à l'appel lambda? Dans mon cas ce n'est PAS

L'application Web héberge sous Tomcat/Java 8 (web.xml version = "3.1" metadata-complete = "true"). À mon ordinateur portable local (Tomcat 8.0.35 ou 8.5.15, Java 8 Mise à jour 131, OSX) J'observe un dépassement de pile pour un tel code de servlet:

public class A extends B { 
    @Override 
    protected void service(HttpServletRequest pRequest, HttpServletResponse pResponse) throws ServletException, IOException { 
     ServletCommon.servletServiceWrapper(pRequest, pResponse, super::service); 
    } 
} 

Le débordement de la pile (voir ci-dessous) semble très suspect - il ne contient pas du tout la classe B dans la hiérarchie! appels de classe A HttpServlet directement (Pourquoi !? Et comment cela possible! ????).

Dans le même code temporel avec lambda fonctionne très bien:

public class A extends B { 
    @Override 
    protected void service(HttpServletRequest pRequest, HttpServletResponse pResponse) throws ServletException, IOException { 
     ServletCommon.servletServiceWrapper(pRequest, pResponse, (t, u) -> super.service(t, u)); 
    } 
} 

L'emballage contient également un code très simple:

public class ServletCommon { 
    @FunctionalInterface 
    public interface MyBiConsumer<T, U>{ 
     void accept(T t, U u) throws ServletException, IOException ; 
    } 
public static void servletServiceWrapper(HttpServletRequest request, HttpServletResponse response, 
     MyBiConsumer<HttpServletRequest, HttpServletResponse> pDelegate) throws ServletException, IOException { 
     pDelegate.accept(request, response); 
    } 
} 

trace de la pile

java.lang.StackOverflowError 
    ServletA.lambda$service$0(ServletA.java:19) 
    ServletCommon.servletServiceWrapper(ServletCommon.java:31) 
    ServletA.service(ServletA.java:19) 
    javax.servlet.http.HttpServlet.service(HttpServlet.java:729) 
    ServletA.lambda$service$0(ServletA.java:19) 
    ServletCommon.servletServiceWrapper(ServletCommon.java:31) 
    ServletA.service(ServletA.java:19) 
    javax.servlet.http.HttpServlet.service(HttpServlet.java:729) 
    ... 

Toutes les idées?

+1

Pouvez-vous construire un [test-case minimal] (http://stackoverflow.com/help/mcve)? Je ne peux pas reprocher ce comportement: http://ideone.com/khkDCK. –

Répondre

1

Pretty much, yeah.

Vous avez réussi à appeler la mauvaise méthode service() (celle publique avec ServletRequest, pas la protégée avec HttpServletRequest), ce qui entraîne une boucle infinie. Cependant, votre code d'exemple n'est pas assez clair pour indiquer pourquoi cela se produit.

+0

Vous avez raison, mais pourquoi "super :: service" pointe vers HttpServlet.service (ServletRequest req, ServletResponse res), mais "(t, u) -> super.service (t, u)" pointent vers " B.service (HttpServletRequest pRequest, HttpServletResponse pResponse) ". J'observe que le comportement est différent de l'environnement d'exécution. (par exemple, Eclipse, Tomcat, Jetty, etc.) – FoxyBOA

+1

Pourriez-vous essayer le cas de test le plus simple possible et voir si le comportement est vraiment différent entre les environnements? Cela pourrait justifier un rapport de bogue, si la résolution de la méthode fonctionne de manière non déterministe. – Kayaman

+2

Cela ressemble à un bogue du compilateur. Je n'ai pas trouvé de version 'javac' ayant ce bug, donc peut-être, il y a une version' ecj' avec un tel bug ... – Holger