参考链接: 关于Java中null的有趣事实
java 计算协方差
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 {
@Override
public 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
@Override
public AbstractTable join(Table table) {
return null;
}
/**
* Some internal API method, also package private
*/
void doThings() {}
void doMoreThings() {
// Use the internal API
join(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 compiler
Table table1 = new CustomTable();
Table join1 = table1.join(joined);
// This works, even if join exposes AbstractTable
CustomTable table2 = new CustomTable();
Table join2 = table2.join(joined);
// This doesn't work. The type AbstractTable is not visible
Table join3 = table2.join(joined).join(joined);
// ^^^^^^^^^^^^^^^^^^^ This cannot be dereferenced
// ... so hide these implementation details again
// The API flaw can be circumvented with casting
Table 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
java 计算协方差