The Container already implements the HttpServletResponse interface; that’s what you get in the doFilter() and service() methods. But to get this compression filter working, we have to make our own custom implementation of the HttpServletResponse interface and pass that to the servlet via the chain.doFilter() call. And that custom implementation has to also include a custom output stream as well, since that’s the goal—to capture the output after the servlet writes to it but before it goes back to the client.
Q: Filters pass ServletRequest and ServletResponse objects to the next thing in the chain, NOT HttpServlet Response! So why are you talking about implementing HttpServletResponse?
A: Filters were designed to be generic, and so officially, you’re right. If we thought one of our filters might be used in a non-web app, we’d be implementing the non-HTTP interface (ServletResponse), but today, the chances of someone developing non-HTTP servlets is close to zero, so we’re not worried. And since ServletResponse is the supertype of HttpServletResponse, there’s no problem passing an HttpServletResponse where a ServletResponse is expected.