有什么方法可以声明内部 class 与外部具有相同的泛型类型?
Is there any way to declare that an inner class has the same generic type as the outer?
有没有办法让内部 class 重用外部 class 的通用参数?
我有以下class
public class TaggedUnionBuilder<L,R>{
private final Class<L> leftClass;
private final Class<R> rightClass;
public TaggedUnionBuilder( Class< L > leftClass, Class< R > rightClass ) {
this.leftClass = leftClass;
this.rightClass = rightClass;
}
TaggedUnion<L,R> left( L aLeft ){
return new TaggedUnion<>( true, aLeft );
}
TaggedUnion<L,R> right( R aRight ){
return new TaggedUnion<>( false, aRight );
}
public class TaggedUnion<L,R>{
private final boolean isLeftClass;
private final Object value;
private TaggedUnion( boolean isLeftClass, Object value ) {
this.isLeftClass = isLeftClass;
this.value = value;
}
L left(){
//vvv compiler error vvv
return leftClass.cast( value );
//^^^ compiler error ^^^
}
R right(){
//vvv compiler error vvv
return rightClass.cast( value );
//^^^ compiler error ^^^
}
boolean isLeft(){
return isLeftClass;
}
boolean isRight(){
return !isLeftClass;
}
}
}
但是,我遇到了错误:
java: incompatible types: L cannot be converted to L
java: incompatible types: R cannot be converted to R
在 left()
和 right()
方法中。
似乎 L
和 R
在 TaggedUnion
中的声明隐藏了 TaggedUnionBuilder
中的声明。如果我删除 TaggedUnion
的泛型参数声明,我不能 return 它作为泛型类型。
如果我从 TaggedUnion
中删除通用参数,我会得到错误
java: type TaggedUnionBuilder.TaggedUnion does not take parameters
一个解决方法是使内部 class TaggedUnion
成为 static class
并显式传入 TaggedUnionBuilder
的实例。这将解决问题。
public class TaggedUnionBuilder<L,R>{
private final Class<L> leftClass;
private final Class<R> rightClass;
public TaggedUnionBuilder( Class< L > leftClass, Class< R > rightClass ) {
this.leftClass = leftClass;
this.rightClass = rightClass;
}
public TaggedUnion<L,R> left( L aLeft ){
return new TaggedUnion<>( this, true, aLeft );
}
public TaggedUnion<L,R> right( R aRight ){
return new TaggedUnion<>( this, false, aRight );
}
public static class TaggedUnion<L,R>{
private final boolean isLeftClass;
private final Object value;
private final TaggedUnionBuilder<L,R> parent;
private TaggedUnion( TaggedUnionBuilder<L,R> aParent, boolean isLeftClass, Object aValue ) {
this.parent = aParent;
this.isLeftClass = isLeftClass;
this.value = aValue;
}
public L left(){
return parent.leftClass.cast( value );
}
public R right(){
return parent.rightClass.cast( value );
}
public boolean isLeft(){
return isLeftClass;
}
public boolean isRight(){
return !isLeftClass;
}
}
}
但有效地使 TaggedUnion
成为一个单独的 class(只需要为 TaggedUnionBuilder
上的 classes 创建访问器)。
问题的真正答案(由@lexicore 在评论中给出)是使用父名称限定返回的类型,如下所示:
public class TaggedUnionBuilder<A,B>{
private final Class<A> classA;
private final Class<B> classB;
public TaggedUnionBuilder( Class< A > classA, Class< B > classB ) {
this.classA = classA;
this.classB = classB;
}
public TaggedUnionBuilder<A,B>.TaggedUnion left( A aA ){
return new TaggedUnion( true, aA );
}
public TaggedUnionBuilder<A,B>.TaggedUnion right( B aB ){
return new TaggedUnion( false, aB );
}
public class TaggedUnion{
private final boolean isClassA;
private final Object value;
private TaggedUnion( boolean isClassA, Object aValue ) {
this.isClassA = isClassA;
this.value = aValue;
}
A left(){
return classA.cast( value );
}
B right(){
return classB.cast( value );
}
boolean isA(){
return isClassA;
}
boolean isB(){
return !isClassA;
}
}
}
但这意味着您不能在没有的情况下一般地引用TaggedUnion
,包括类型中包含的class(即总是TaggedUnionBuilder<A,B>.TaggedUnion
而不是TaggedUnion
) 这在人体工程学上是残酷的,但回答了所述问题。
因为你有一个嵌套的 class,泛型类型也 "propagated" 到嵌套的 class 中。
public class Outer<A> {
Inner in;
Outer( A a ) {
in = new Inner( a );
}
Inner get() {
return in;
}
class Inner {
A value;
Inner( A value ) {
this.value = value;
}
}
public static void main( String[] args ) {
String s = new Outer<>( "test" ).get().value;
//And to declare an variable Inner
Outer<String>.Inner i = new Outer<>( "test" ).get();
}
}
声明一个 Outer<String>
将得到一个 Outer<String>.Inner
,value
将是一个 String
。
如果你声明一个 Inner
,你需要使用完整的 class 名称来指定泛型类型,有点冗长但正确的解决方案。
前言
首先让我怀疑 class 像你展示的那样的有用性。如果能够将一种类型转换为另一种类型,则至少有一种类型是接口,或者两种类型都处于继承关系中。在我看来,您可以使用 Class#isAssignableForm(Object obj)
, Class#isInstance(Object obj)
and Class#cast(Object obj)
.
完成所有这些操作
部署 Builder Pattern 时,通常将构建器编写为实际类型的内部静态 class。这有助于提高可读性(重要的 class 是 POJO,而不是构建器)。此外,内部 (non-static) class 始终隐式引用周围的实例 class 它是从中创建的。
有没有办法让内部类重用外部类的泛型参数class?
是的。让我们看一下您的代码的一些简化版本。
public class TaggedUnionBuilder<A, B> {
// ...
public class TaggedUnion {
// ...
}
}
正如我们所知,我们通过首先实例化外部 class 的实例然后在该实例上调用 new <InnerClass>()
来实例化内部 classes:
TaggedUnionBuilder<String, Object> builder = new TaggedUnionBuilder<String, Object>();
TaggedUnionBuilder<String, Object>.TaggedUnion union = builder.new TaggedUnion();
写到这里,一看union
的完整类型确实是TaggedUnionBuilder<String, Object>.TaggedUnion
.
这对实施意味着什么?
我们可以在 TaggedUnion
中使用通用参数 A
和 B
。因此,我们可以将代码重构为:
public class TaggedUnionBuilder<A, B> {
private final Class<A> classA;
private final Class<B> classB;
public TaggedUnionBuilder(Class<A> classA, Class<B> classB) {
this.classA = classA;
this.classB = classB;
}
TaggedUnion left(A a) {
return new TaggedUnion(true, a);
}
TaggedUnion right(B b) {
return new TaggedUnion(false, b);
}
public class TaggedUnion {
private final boolean isClassA;
private final Object value;
private TaggedUnion(boolean isClassA, Object value) {
this.isClassA = isClassA;
this.value = value;
}
A left() {
return classA.cast(value);
}
B right() {
return classB.cast(value);
}
boolean isA() {
return isClassA;
}
boolean isB() {
return !isClassA;
}
}
}
有没有办法让内部 class 重用外部 class 的通用参数?
我有以下class
public class TaggedUnionBuilder<L,R>{
private final Class<L> leftClass;
private final Class<R> rightClass;
public TaggedUnionBuilder( Class< L > leftClass, Class< R > rightClass ) {
this.leftClass = leftClass;
this.rightClass = rightClass;
}
TaggedUnion<L,R> left( L aLeft ){
return new TaggedUnion<>( true, aLeft );
}
TaggedUnion<L,R> right( R aRight ){
return new TaggedUnion<>( false, aRight );
}
public class TaggedUnion<L,R>{
private final boolean isLeftClass;
private final Object value;
private TaggedUnion( boolean isLeftClass, Object value ) {
this.isLeftClass = isLeftClass;
this.value = value;
}
L left(){
//vvv compiler error vvv
return leftClass.cast( value );
//^^^ compiler error ^^^
}
R right(){
//vvv compiler error vvv
return rightClass.cast( value );
//^^^ compiler error ^^^
}
boolean isLeft(){
return isLeftClass;
}
boolean isRight(){
return !isLeftClass;
}
}
}
但是,我遇到了错误:
java: incompatible types: L cannot be converted to L
java: incompatible types: R cannot be converted to R
在 left()
和 right()
方法中。
似乎 L
和 R
在 TaggedUnion
中的声明隐藏了 TaggedUnionBuilder
中的声明。如果我删除 TaggedUnion
的泛型参数声明,我不能 return 它作为泛型类型。
如果我从 TaggedUnion
中删除通用参数,我会得到错误
java: type TaggedUnionBuilder.TaggedUnion does not take parameters
一个解决方法是使内部 class TaggedUnion
成为 static class
并显式传入 TaggedUnionBuilder
的实例。这将解决问题。
public class TaggedUnionBuilder<L,R>{
private final Class<L> leftClass;
private final Class<R> rightClass;
public TaggedUnionBuilder( Class< L > leftClass, Class< R > rightClass ) {
this.leftClass = leftClass;
this.rightClass = rightClass;
}
public TaggedUnion<L,R> left( L aLeft ){
return new TaggedUnion<>( this, true, aLeft );
}
public TaggedUnion<L,R> right( R aRight ){
return new TaggedUnion<>( this, false, aRight );
}
public static class TaggedUnion<L,R>{
private final boolean isLeftClass;
private final Object value;
private final TaggedUnionBuilder<L,R> parent;
private TaggedUnion( TaggedUnionBuilder<L,R> aParent, boolean isLeftClass, Object aValue ) {
this.parent = aParent;
this.isLeftClass = isLeftClass;
this.value = aValue;
}
public L left(){
return parent.leftClass.cast( value );
}
public R right(){
return parent.rightClass.cast( value );
}
public boolean isLeft(){
return isLeftClass;
}
public boolean isRight(){
return !isLeftClass;
}
}
}
但有效地使 TaggedUnion
成为一个单独的 class(只需要为 TaggedUnionBuilder
上的 classes 创建访问器)。
问题的真正答案(由@lexicore 在评论中给出)是使用父名称限定返回的类型,如下所示:
public class TaggedUnionBuilder<A,B>{
private final Class<A> classA;
private final Class<B> classB;
public TaggedUnionBuilder( Class< A > classA, Class< B > classB ) {
this.classA = classA;
this.classB = classB;
}
public TaggedUnionBuilder<A,B>.TaggedUnion left( A aA ){
return new TaggedUnion( true, aA );
}
public TaggedUnionBuilder<A,B>.TaggedUnion right( B aB ){
return new TaggedUnion( false, aB );
}
public class TaggedUnion{
private final boolean isClassA;
private final Object value;
private TaggedUnion( boolean isClassA, Object aValue ) {
this.isClassA = isClassA;
this.value = aValue;
}
A left(){
return classA.cast( value );
}
B right(){
return classB.cast( value );
}
boolean isA(){
return isClassA;
}
boolean isB(){
return !isClassA;
}
}
}
但这意味着您不能在没有的情况下一般地引用TaggedUnion
,包括类型中包含的class(即总是TaggedUnionBuilder<A,B>.TaggedUnion
而不是TaggedUnion
) 这在人体工程学上是残酷的,但回答了所述问题。
因为你有一个嵌套的 class,泛型类型也 "propagated" 到嵌套的 class 中。
public class Outer<A> {
Inner in;
Outer( A a ) {
in = new Inner( a );
}
Inner get() {
return in;
}
class Inner {
A value;
Inner( A value ) {
this.value = value;
}
}
public static void main( String[] args ) {
String s = new Outer<>( "test" ).get().value;
//And to declare an variable Inner
Outer<String>.Inner i = new Outer<>( "test" ).get();
}
}
声明一个 Outer<String>
将得到一个 Outer<String>.Inner
,value
将是一个 String
。
如果你声明一个 Inner
,你需要使用完整的 class 名称来指定泛型类型,有点冗长但正确的解决方案。
前言
首先让我怀疑 class 像你展示的那样的有用性。如果能够将一种类型转换为另一种类型,则至少有一种类型是接口,或者两种类型都处于继承关系中。在我看来,您可以使用 Class#isAssignableForm(Object obj)
, Class#isInstance(Object obj)
and Class#cast(Object obj)
.
部署 Builder Pattern 时,通常将构建器编写为实际类型的内部静态 class。这有助于提高可读性(重要的 class 是 POJO,而不是构建器)。此外,内部 (non-static) class 始终隐式引用周围的实例 class 它是从中创建的。
有没有办法让内部类重用外部类的泛型参数class?
是的。让我们看一下您的代码的一些简化版本。
public class TaggedUnionBuilder<A, B> {
// ...
public class TaggedUnion {
// ...
}
}
正如我们所知,我们通过首先实例化外部 class 的实例然后在该实例上调用 new <InnerClass>()
来实例化内部 classes:
TaggedUnionBuilder<String, Object> builder = new TaggedUnionBuilder<String, Object>();
TaggedUnionBuilder<String, Object>.TaggedUnion union = builder.new TaggedUnion();
写到这里,一看union
的完整类型确实是TaggedUnionBuilder<String, Object>.TaggedUnion
.
这对实施意味着什么?
我们可以在 TaggedUnion
中使用通用参数 A
和 B
。因此,我们可以将代码重构为:
public class TaggedUnionBuilder<A, B> {
private final Class<A> classA;
private final Class<B> classB;
public TaggedUnionBuilder(Class<A> classA, Class<B> classB) {
this.classA = classA;
this.classB = classB;
}
TaggedUnion left(A a) {
return new TaggedUnion(true, a);
}
TaggedUnion right(B b) {
return new TaggedUnion(false, b);
}
public class TaggedUnion {
private final boolean isClassA;
private final Object value;
private TaggedUnion(boolean isClassA, Object value) {
this.isClassA = isClassA;
this.value = value;
}
A left() {
return classA.cast(value);
}
B right() {
return classB.cast(value);
}
boolean isA() {
return isClassA;
}
boolean isB() {
return !isClassA;
}
}
}