java - 自类型 - 需要类型绑定内的类型
问题描述
我想知道是否可以限制在接口上声明的方法要求类型绑定内的类型。实际上,我想提供一种在无法提供真正类型安全的情况下强制转换类型安全的方法。
例如,考虑具有Base
通过中间接口继承的基本类型的层次结构。通常,人们知道一种类型是接口FirstInterface
,但不知道实现它的特定类。我想要一种方法,该方法允许转换为接口的任一实现,而不允许转换为其他实现,Base
如以下示例所示:
interface Base<TYPE extends Base<TYPE>> {
default <CAST extends TYPE> CAST as(Class<? extends CAST> type) {
if (type.isInstance(this)) {
return type.cast(this);
} else {
throw new IllegalArgumentException();
}
}
}
interface FirstIface<TYPE extends FirstIface<TYPE>> extends Base<TYPE> { }
class FirstClassA implements FirstIface<FirstClassA> { }
class FirstClassB implements FirstIface<FirstClassB> { }
interface SecondIface<TYPE extends SecondIface<TYPE>> extends Base<TYPE> { }
class SecondClassA implements SecondIface<SecondClassA> { }
class SecondClassB implements SecondIface<SecondClassB> { }
interface ThirdIface<TYPE extends ThirdIface<TYPE>> extends FirstIface<TYPE>, SecondIface<TYPE> { }
class ThirdClassA implements ThirdIface<ThirdClassA> { }
class ThirdClassB implements ThirdIface<ThirdClassB> { }
我希望能够在 Java 中编译以下代码:
FirstIface<?> i = new FirstClassA();
FirstClassA a = i.as(FirstClassA.class); // desired: compiles, now: compiler error
FirstClassB b = i.as(FirstClassB.class); // desired: runtime exception, now: compiler error
这同样适用于 的层次结构ThirdIFace
,而以下代码应呈现编译器错误:
SecondIface<?> i = new SecondClassA();
SecondClassA a = i.as(SecondClassA.class); // now and desired: compiler error
SecondClassB b = i.as(SecondClassB.class); // now and desired: compiler error
有什么方法可以声明Base.as
不执行此要求吗?代码是自动生成的,因此也可以在自动生成的接口中提供覆盖(就像类一样)。使用覆盖时,会出现SecondIface extends FirstIface
.
解决方案
如果您的目标是只允许可能成功的调用,并且可能成功意味着我们(静态地)知道这SUB
是SUPER
尝试将 type 的实例强制转换为 type 时的子SUPER
类型SUB
,我不确定这是可能的。
一个问题,即使在克服了没有类型变量的问题之后,比如说,中间的 self 类型(FirstIface<?>
在你的例子中),编译器会在必要时推断SUPER==Object
(或)满足.SUPER==Base<?>
SUB extends SUPER
代码是自动生成的,因此也可以在自动生成的接口中提供覆盖
我认为这不会有帮助。目标是让子接口中的方法具有比超类型中声明的更严格的参数类型,但参数类型不是协变的(协变参数类型会违反 Liskov 替换原则)
但由于通配符可以是 FirstIface 的任何子类型,它不允许 FirstIface 的任何子类型
是的。我们只有 1)TYPE
用于最终实现类型的 self 类型变量,以及 2)用于Base
接口的类型。我们没有办法记下调用站点已知的中间类型信息的类型。
我怀疑最接近您的目标的是:
- 对演员表使用静态方法,并且
- 避免在调用站点使用类型推断,因为 javac 很乐意推断
Object
超类型以使代码编译。
当然,这不是一个好的解决方案,因为避免类型推断是不切实际的。这是一个完整的例子:
public class Hello {
interface Base<TYPE extends Base<TYPE>> {}
interface FirstIface<TYPE extends FirstIface<TYPE>> extends Base<TYPE> {}
static final class FirstClassA implements FirstIface<FirstClassA> { }
static final class FirstClassB implements FirstIface<FirstClassB> { }
interface SecondIface<TYPE extends SecondIface<TYPE>> extends Base<TYPE> { }
static final class SecondClassA implements SecondIface<SecondClassA> { }
static final class SecondClassB implements SecondIface<SecondClassB> { }
public static void main(String[] args) {
FirstIface<?> i = new FirstClassA();
FirstClassA a = Hello.<FirstIface<?>, FirstClassA>as(i, FirstClassA.class); // works
FirstClassB b = Hello.<FirstIface<?>, FirstClassB>as(i, FirstClassB.class); // runtime error
SecondClassA c = Hello.<FirstIface<?>, SecondClassA>as(i, SecondClassA.class); // compile error
SecondClassB d = Hello.<FirstIface<?>, SecondClassB>as(i, SecondClassB.class); // compile error
}
static <SUPER, SUB extends SUPER> SUB as(SUPER obj, Class<? extends SUB> c) {
return (SUB) obj;
}
}
推荐阅读
- amazon-web-services - 将 AWS IoT 与本地 Mosquitto MQTT 桥接时出现“证书验证失败”
- api - 向 Swashbuckle 路径中的查询字符串添加参数
- java - 如何在没有用户输入的情况下自动返回主菜单?
- java - 每次添加具有相同变量但不同值的列表视图
- c++ - IDXGISurface::Map:此对象不是使用允许 CPU 访问的 CPUAccess 标志创建的
- react-native - 世博隧道未启动
- python - 如何更改 matplotlib 中误差线限制的标记符号?
- excel - 如何将文件从一个 SharePoint 文件夹移动到另一个文件夹
- git - git:从特定分支镜像存储库(bitbucket)
- javascript - Vue.js - 仅打印值而不是 JSON 键值表示法?