为什么这种从超级接口到子接口的转换有效?

Why does this casting from a super to a sub-interface work?

我正在从 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() {}

我对这一行感到困惑:

HttpServletResponse response = (HttpServletResponse) res;

我理解从 ServletResponseHttpServletResponse 的转换是必要的,因为后一个接口有 #setHeader()。但是为什么这个演员表有效?是否因为传递给该方法的基础对象是 HttpServletResponse 而起作用?

但是除了运行时之外,为什么编译器允许这样做?我是 Java 的新手,并且预计此转换会失败,因为它是从不太具体的类型到更具体的类型。例如,使用 Element and Vertex,我遇到了这个失败:

Vertex v = (Vertex) e; // `e` is an `Element`

The JLS 几乎完美地解释了它。

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.

即当您进行此类转换时,您 明确地将类型 S 转换为类型 T。这样做是在告诉编译器“我确定这是可以的”。

如果您认为它 可能 失败,那么您应该 try...catch ClassCastException 并停止您的程序意外终止。