Java有时可能非常棘手,特别是在API设计中。 让我们看一个非常有趣的展示柜。 jOOQ强烈地将API与实现分开。 所有API都在org.jooq包中,并且是公共的。 大多数实现都在org.jooq.impl包和package-private中。 只有工厂和一些专用的基础实现是公开的。 这允许非常强大的包级封装,几乎只向jOOQ用户公开接口。
包级封装的简化示例
大致来说,jOOQ如何建模SQL表。 (过于简化的)API:
package org.jooq;/*** A table in a database*/
public interface Table {/*** Join two tables*/Table join(Table table);
}
还有两个(过于简化的)实现类:
package org.jooq.impl;import org.jooq.Table;/*** Base implementation*/
abstract class AbstractTable implements Table {@Overridepublic Table join(Table table) {return null;}
}/*** Custom implementation, publicly exposed to client code*/
public class CustomTable extends AbstractTable {
}
内部API的公开方式
假设内部API在协方差方面有一些技巧:
abstract class AbstractTable implements Table, InteralStuff {// Note, this method returns AbstractTable, as it might// prove to be convenient to expose some internal API// facts within the internal API itself@Overridepublic AbstractTable join(Table table) {return null;}/*** Some internal API method, also package private*/void doThings() {}void doMoreThings() {// Use the internal APIjoin(this).doThings();}
}
乍一看,这看起来很安全,是吗? AbstractTable是包私有的,但是CustomTable对其进行了扩展并继承了其所有API,包括“ AbstractTable join(Table)”的协变方法重写。 这会导致什么? 查看以下客户代码
package org.jooq.test;import org.jooq.Table;
import org.jooq.impl.CustomTable;public class Test {public static void main(String[] args) {Table joined = new CustomTable();// This works, no knowledge of AbstractTable exposed to the compilerTable table1 = new CustomTable();Table join1 = table1.join(joined);// This works, even if join exposes AbstractTableCustomTable table2 = new CustomTable();Table join2 = table2.join(joined);// This doesn't work. The type AbstractTable is not visibleTable join3 = table2.join(joined).join(joined);// ^^^^^^^^^^^^^^^^^^^ This cannot be dereferenced// ... so hide these implementation details again// The API flaw can be circumvented with castingTable join4 = ((Table) table2.join(joined)).join(joined);}
}
结论
篡改类层次结构中的可见性可能很危险。 注意以下事实:在接口中声明的API方法始终是公共的,而不管涉及非公共工件的任何协变实现。 如果API设计人员无法正确处理API用户,这可能会很烦人。
在下一版的jOOQ中已修复
参考: Java的深度:在JAVA,SQL和JOOQ博客中, JCG合作伙伴 Lukas Eder 通过协方差暴露了API泄漏 。
翻译自: https://www.javacodegeeks.com/2012/05/depths-of-java-api-leak-exposed-through.html