0

I'm looking at this example from a Spring Boot guide:

@Component
public class SimpleCORSFilter implements Filter {

    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
        HttpServletResponse response = (HttpServletResponse) res;
        response.setHeader("Access-Control-Allow-Origin", "*");
        response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
        response.setHeader("Access-Control-Max-Age", "3600");
        response.setHeader("Access-Control-Allow-Headers", "x-requested-with");
        chain.doFilter(req, res);
    }

    public void init(FilterConfig filterConfig) {}

    public void destroy() {}

And am confused by this line:

HttpServletResponse response = (HttpServletResponse) res;

I understand the cast from ServletResponse to HttpServletResponse is necessary because the latter interface has #setHeader(). But why does this cast work? Does it work because the underlying object passed to the method is a HttpServletResponse?

But runtime aside, why does the compiler allow this? I'm new to Java and would have expected this cast to fail, since it's from a less to more specific type. For example, using Element and Vertex, I've had this fail:

Vertex v = (Vertex) e; // `e` is an `Element`
Dmitry Minkovsky
  • 36,185
  • 26
  • 116
  • 160

1 Answers1

4

The JLS pretty much explains it perfectly.

5.1.6 Narrowing Reference Conversion

Such conversions require a test at run time to find out whether the actual reference value is a legitimate value of the new type. If not, then a ClassCastException is thrown.

i.e. When you do such a conversion you explicitly cast the type S to the type T. By doing so you are telling the compiler "I am sure this is ok".

If you think it might fail, then you should try...catch for a ClassCastException and stop your program terminating unexpectedly.

Community
  • 1
  • 1
Andy Brown
  • 18,961
  • 3
  • 52
  • 62
  • I guess this would be a commonly-requested/desired feature of the spec, but it seems to be poor form if you're looking for type safety? – Dmitry Minkovsky Jan 16 '15 at 19:37
  • @dimadima Not all type safety information can be inferred at compile time. Casting is an *explicit request by the developer* to do something at runtime. The implementation of the super type might not even be in your code, like in the servlet spec. – Dave Newton Jan 16 '15 at 19:43
  • 1
    There are also sometimes when an API can not change the types in the parameters to preserve backwards compatability. So the Java authors had to make new subtypes with additional methods that are **guaranteed** to always work as a downcast. for example in Java Swing the `Graphics` objects can always be downcast to `Graphics2D` http://stackoverflow.com/questions/179415/java2d-is-it-always-safe-to-cast-graphics-into-graphics2d – dkatzel Jan 16 '15 at 19:47
  • @dkatzel thanks for this. This is a great example to my question of "why". The answer here says that "The JLS pretty much explain it perfectly." I don't know why describing the behavior means it's explained perfectly. In fact, its only your example that really explains WHY. – Dmitry Minkovsky Jan 16 '15 at 20:24
  • 1
    @dimadima "*why does this cast work?*": the JLS specifies that it is allowed to (as per the detail in my answer). "*why does the compiler allow this?*": because it obeys/enforces the JLS. Perhaps your third "*why*" is "*why does the JLS allow NRCs to cause a CCE*": the short answer is that it was the way it was designed; without this many features in the language would require redesign. – Andy Brown Jan 16 '15 at 20:38