Java 中的参数化格式良好和捕获转换
Parameterization Well Formedness and Capture Conversion in Java
给定以下两个 class 定义:
class C1<T extends C1<T>> {}
class C2<U> extends C1<C2<U>> {}
以及以下类型声明:
C1<C2<?>> a;
直觉上感觉声明的类型 a
应该是有效的,但这不是 JDK-8u45 的行为方式。相反,我们得到类似以下输出的内容:
Test.java:3: error: type argument C2<?> is not within bounds of type-variable T
C1<C2<?>> a;
^
where T is a type-variable:
T extends C1<T> declared in class C1
1 error
(编辑:我在这里是个笨蛋,这部分已经回答了:C2<?>
不扩展C1<C2<?>>
。下面关于 c
声明的问题仍然是一个悬而未决的问题。)
但是 C2<?>
确实 扩展了 C1<C2<?>>
,这似乎很容易满足界限。据我所知,对 JLS 的检查没有提供进一步的启发。它真的应该像满足子类型关系的约束一样简单,因为 C2<?>
不是通配符类型,因此捕获转换只是参数的身份转换。
有些情况会变得不太清楚,例如,采用以下class定义:
class C3<T extends C3<?>> {}
class C4<Y, Z> extends C3<C4<Z, Y>> {}
class C5<X extends C3<X>> {
void accept(X x);
}
所有这些都很好,但是如果我们尝试以下声明:
C5<C6<?, ?>> b;
事情变得陌生了。 C6<?, ?>
是 C3<C6<?, ?>>
的子类型,因此根据我对上面给出的关于声明 C1<C2<?>>
的规范的解释,声明应该是有效的。问题是显然并不是 C6<?, ?>
的每个可能的子类型实际上都满足该界限,所以现在例如 C5.accept()
将其参数类型解析为 C6<?, ?>
并且因此可以接受违反 C6<?, ?>
界限的参数=30=],即 Y
和 Z
的参数化不相同的任何地方。
我哪里错了?是不是我对子类型关系的理解不够?
(编辑: 问题的以下部分仍未得到解答,但我已将其移至新问题 因为它确实是一个完全不同的问题...很抱歉弄得一团糟,没有很好地使用该网站哈哈...)
除此之外,我在类似情况下的捕获转换也遇到了一些问题。采用以下类型声明:
C1<? extends C2<?>> c;
与开头的类似声明 a
不同,这在 JDK-8u45 中编译良好。但是,如果我们检查 specification for capture conversion,这次声明 应该 会导致编译时错误。
特别是,新类型变量捕获的上限 CAP#T
由 glb(Bi, Ui[A1:=S1,...,An:=Sn])
给出,在这种情况下 Bi
解析为通配符边界 C2<?>
Ui[A1:=S1,...,An:=Sn]
解析为 C1<CAP#T>
.
由此,glb(C2<?>, C1<CAP#T>)
解析为交集类型C2<?> & C1<CAP#T>
,这是无效的,因为C2<?>
和C1<CAP#T>
都是class类型,不是接口类型,但它们都不是另一个的子类型。
这种(明显的)违规行为在 definition of the intersection type 本身中更加明显。
我确定这不是一个错误,我只是在某个地方犯了一些简单的错误...但是如果这里没有人可以为我阐明这一点,我会尝试编译器开发邮件列表或其他东西.
感谢您的帮助!
同时
C2<x> extends C1<C2<x>>
对于任何引用类型 x
、
并非如此
C2<?> extends C1<C2<?>>
通配符 ?
不是类型。这是一个 类型的参数 。虽然语法非常具有欺骗性(设计使然)。
让我们使用不同的语法 - 如果有任何一级通配符,请使用 {}
而不是 <>
,例如
List{?}, Map{String, ? extends Number}
{?}
的意思是声明一个union类型
List{? extends Number} == union of List<Number>, List<Integer>, List<Long>, ....
不难看出,List<Integer>
是List{? extends Number}
的子类型;
List{? extends Number}
是 List{? extends Object}
的子类型
但是,Foo{?}
不可能是 Foo<x>
的子类型。
在我们的语法中,<>
保留用于用 types 替换类型变量。所以我们写
List<String>, C2<Integer>
,等等。很容易理解它们的意思——只要把List
源码中的T
换成String
,就可以得到一个好老的普通class.
interface List<String>
String get(int)
这不能用于通配符 - 它没有意义
interface List<?>
? get(int)
所以不允许new ArrayList{?}()
,或者class MyList implements List{?}
那么,我们如何使用List{?}
呢?我们可以调用哪些方法?
当一个表达式的类型是一个List{?}
时,我们知道它是一个对象,而这个对象一定属于一个子class List<x>
的一些未知 类型 x
。这是通配符捕获
obj is a List{?} => obj is a List<x>, where x a subtype of Object.
即使 x
的确切类型在编译时未知,我们仍然可以进行替换
interface List<x>
x get(int)
所以我们可以理解调用 obj.get(0)
;它 returns x
,并且 x
是 Object
的子类型;所以我们可以将 return 值分配给 Object
.
给定以下两个 class 定义:
class C1<T extends C1<T>> {}
class C2<U> extends C1<C2<U>> {}
以及以下类型声明:
C1<C2<?>> a;
直觉上感觉声明的类型 a
应该是有效的,但这不是 JDK-8u45 的行为方式。相反,我们得到类似以下输出的内容:
Test.java:3: error: type argument C2<?> is not within bounds of type-variable T
C1<C2<?>> a;
^
where T is a type-variable:
T extends C1<T> declared in class C1
1 error
(编辑:我在这里是个笨蛋,这部分已经回答了:C2<?>
不扩展C1<C2<?>>
。下面关于 c
声明的问题仍然是一个悬而未决的问题。)
但是 C2<?>
确实 扩展了 C1<C2<?>>
,这似乎很容易满足界限。据我所知,对 JLS 的检查没有提供进一步的启发。它真的应该像满足子类型关系的约束一样简单,因为 C2<?>
不是通配符类型,因此捕获转换只是参数的身份转换。
有些情况会变得不太清楚,例如,采用以下class定义:
class C3<T extends C3<?>> {}
class C4<Y, Z> extends C3<C4<Z, Y>> {}
class C5<X extends C3<X>> {
void accept(X x);
}
所有这些都很好,但是如果我们尝试以下声明:
C5<C6<?, ?>> b;
事情变得陌生了。 C6<?, ?>
是 C3<C6<?, ?>>
的子类型,因此根据我对上面给出的关于声明 C1<C2<?>>
的规范的解释,声明应该是有效的。问题是显然并不是 C6<?, ?>
的每个可能的子类型实际上都满足该界限,所以现在例如 C5.accept()
将其参数类型解析为 C6<?, ?>
并且因此可以接受违反 C6<?, ?>
界限的参数=30=],即 Y
和 Z
的参数化不相同的任何地方。
我哪里错了?是不是我对子类型关系的理解不够?
(编辑: 问题的以下部分仍未得到解答,但我已将其移至新问题
除此之外,我在类似情况下的捕获转换也遇到了一些问题。采用以下类型声明:
C1<? extends C2<?>> c;
与开头的类似声明 a
不同,这在 JDK-8u45 中编译良好。但是,如果我们检查 specification for capture conversion,这次声明 应该 会导致编译时错误。
特别是,新类型变量捕获的上限 CAP#T
由 glb(Bi, Ui[A1:=S1,...,An:=Sn])
给出,在这种情况下 Bi
解析为通配符边界 C2<?>
Ui[A1:=S1,...,An:=Sn]
解析为 C1<CAP#T>
.
由此,glb(C2<?>, C1<CAP#T>)
解析为交集类型C2<?> & C1<CAP#T>
,这是无效的,因为C2<?>
和C1<CAP#T>
都是class类型,不是接口类型,但它们都不是另一个的子类型。
这种(明显的)违规行为在 definition of the intersection type 本身中更加明显。
我确定这不是一个错误,我只是在某个地方犯了一些简单的错误...但是如果这里没有人可以为我阐明这一点,我会尝试编译器开发邮件列表或其他东西.
感谢您的帮助!
同时
C2<x> extends C1<C2<x>>
对于任何引用类型 x
、
并非如此
C2<?> extends C1<C2<?>>
通配符 ?
不是类型。这是一个 类型的参数 。虽然语法非常具有欺骗性(设计使然)。
让我们使用不同的语法 - 如果有任何一级通配符,请使用 {}
而不是 <>
,例如
List{?}, Map{String, ? extends Number}
{?}
的意思是声明一个union类型
List{? extends Number} == union of List<Number>, List<Integer>, List<Long>, ....
不难看出,List<Integer>
是List{? extends Number}
的子类型;
List{? extends Number}
是 List{? extends Object}
但是,Foo{?}
不可能是 Foo<x>
的子类型。
在我们的语法中,<>
保留用于用 types 替换类型变量。所以我们写
List<String>, C2<Integer>
,等等。很容易理解它们的意思——只要把List
源码中的T
换成String
,就可以得到一个好老的普通class.
interface List<String>
String get(int)
这不能用于通配符 - 它没有意义
interface List<?>
? get(int)
所以不允许new ArrayList{?}()
,或者class MyList implements List{?}
那么,我们如何使用List{?}
呢?我们可以调用哪些方法?
当一个表达式的类型是一个List{?}
时,我们知道它是一个对象,而这个对象一定属于一个子class List<x>
的一些未知 类型 x
。这是通配符捕获
obj is a List{?} => obj is a List<x>, where x a subtype of Object.
即使 x
的确切类型在编译时未知,我们仍然可以进行替换
interface List<x>
x get(int)
所以我们可以理解调用 obj.get(0)
;它 returns x
,并且 x
是 Object
的子类型;所以我们可以将 return 值分配给 Object
.