繁体   English   中英

有什么方法可以声明内部类与外部类具有相同的泛型类型?

[英]Is there any way to declare that an inner class has the same generic type as the outer?

有什么方法可以通过内部类重用外部类的通用参数吗?


我有以下课程

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:不兼容的类型:L不能转换为L

java:不兼容的类型:R不能转换为R

left()right()方法中。

这似乎是宣告LRTaggedUnion被遮蔽在声明TaggedUnionBuilder 如果删除TaggedUnion的通用参数声明,则无法将其作为通用类型返回。

如果我从TaggedUnion删除通用参数, TaggedUnion收到错误消息

java:类型为TaggedUnionBuilder.TaggedUnion不带参数

解决该问题的方法是使内部类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成为一个单独的类(只需为TaggedUnionBuilder上的类提供TaggedUnionBuilder )。

由于您有一个嵌套类,因此泛型类型也被“传播”到嵌套类中。

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 ,则需要使用完整的类名来指定泛型类型,这虽然冗长但又是正确的解决方案。

所述问题的真正答案(在评论中由@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 genericly 而不包括在类型包含类(即总是TaggedUnionBuilder<A,B>.TaggedUnion代替TaggedUnion ),其是符合人体工程学的残酷但回答了说明。

前言

首先,让我怀疑您所展示的一门课程的用处。 如果一个能够将一种类型转换为另一种类型,则至少一个类型是接口,或者两种类型都处于继承关系中。 从我的角度来看,您可以使用Class#isAssignableForm(Object obj)Class#isInstance(Object obj)Class#cast(Object obj)来完成所有这些工作。

在部署构建器模式时 ,通常会将构建器写为实际类型的内部静态类。 这有助于提高可读性(重要的类是POJO,而不是生成器)。 此外,内部(非静态)类始终对创建它的周围类的实例具有隐式引用。


有什么方法可以通过内部类重用外部分类的通用参数吗?

就在这里。 让我们看一下您的代码的简化版本。

public class TaggedUnionBuilder<A, B> {
    // ...
    public class TaggedUnion {
        // ...
    }
}

众所周知,我们先实例化外部类的实例,然后在该实例上调用new <InnerClass>()来实例化内部类,如下所示:

TaggedUnionBuilder<String, Object> builder = new TaggedUnionBuilder<String, Object>();
TaggedUnionBuilder<String, Object>.TaggedUnion union = builder.new TaggedUnion();

通过编写此代码,可以看到union的完整类型确实是TaggedUnionBuilder<String, Object>.TaggedUnion


这对实施意味着什么?

我们可以使用TaggedUnion已经存在的通用参数AB 因此,我们可以将代码重构为:

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;
        }
    }
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM