有没有办法声明内部类与外部类具有相同的泛型类型?

时间:2018-04-11 19:07:46

标签: java generics inner-classes

有没有办法通过内部类重用外部类的泛型参数?

我有以下课程

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删除通用参数,我会收到错误

  

java:type TaggedUnionBuilder.TaggedUnion不接受参数

4 个答案:

答案 0 :(得分:2)

围绕它进行一项工作,使内部类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上的类创建访问器。)

答案 1 :(得分:2)

由于你有一个嵌套类,所以通用类型是&#34;传播&#34;进入嵌套类。

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>.Innervalue将为String

如果声明Inner,则需要使用完整的类名来指定泛型类型,这是一个更冗长但更正确的解决方案。

答案 2 :(得分:1)

所述问题的真实答案(由@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通用包括类型中的包含类(即始终TaggedUnionBuilder<A,B>.TaggedUnion而不是TaggedUnion)这是残酷的符合人体工程学,但回答了所陈述的问题。

答案 3 :(得分:0)

<强>前言

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

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

有没有办法通过内部类重用外部clas的泛型参数?

是的。让我们看一下代码的简化版本。

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

这对实施意味着什么?

我们可以使用A中已有的通用参数BTaggedUnion。因此,我们可以将代码重构为:

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