简体   繁体   中英

Using generics in non-static inner class

public abstract class OuterClass<OT extends OuterClass<OT>> {
    public <C extends OuterClass<?>> C parse(Class<C> clazz) {
        if (clazz.isInstance(this)) {
            return (C) this;
        }

        return null;
    }

    public abstract class InnerClass<CT extends InnerClass<CT> {
        public <C extends InnerClass<?>> C parse(Class<C> clazz) {
            if (clazz.isInstance(this)) {
                return (C) this;
            }

            return null;
        }
    }
}

OuterClass<?> oInstance;
InnerClass<?> iInstance;

In the above example the iInstance variable works fine. However the iInstance variable shows an error when adding the generics part

Type arguments given on a raw type

If I remove the generics part from variables, then the below test cases will fail with type errors

public class ExtendedOuter extends OuterClass<ExtendedOuter> {

}

// This only works on OuterClass<?> and not on OuterClass
ExtendedOuter eInstance = oInstance.parse(ExtendedOuter.class);

Found: OuterClass, required: ExtendedOuter

This is no problem on static/outer classes as they can be defined as ClassName<?> , but non-static inner classes cannot be defined with <?>

How can I add <?> to iInstance without making InnerClass static?

EDIT: Let me give some examples why these classes uses their extended versions as generic.

public abstract class OuterClass<OT extends OuterClass<OT>> {
    public abstract OT returnMe();
}

public class ExtendedOuter extends OuterClass<ExtendedOuter> {
    @Override
    public ExtendedOuter returnMe() {
        return this;
    }
}

The above for an example would not work if I simply made the return type OuterClass on the abstract version. If so, any extended versions would have to be casted whenever this method was used, which does not seam ideal.

Also just got an error from AndroidStudio as well after removing <CT> in <T extends OuterClass<CT>>

The parameter OT is not within it's bound

This error is shown in extended classes when doing ClassName extends OuterClass<ClassName> . In other words it will not work just using <T extends OuterClass> on the abstract classes.

Similar to a previous post I did which showcased a builder patter which uses generic types and inheritance to reduce the actual code in inheritance cases, this is also possible for none-static classes. I therefore modified the builder example accordingly to avoid static inner-classes:

Parent class with parent builder:

public abstract class TestParam<Z>
{
    public abstract class CommonBuilder<T extends CommonBuilder<T>>
    {
        protected final String a;
        protected final String b;
        protected final String c;
        protected Z z = null;

        public CommonBuilder(String a, String b, String c) 
        {
            this.a = a;
            this.b = b;
            this.c = c;
        }

        @SuppressWarnings("unchecked")
        public T withOptionalZ(Z z)
        {
            this.z = z;
            return (T)this;
        }

        @SuppressWarnings("hiding")
        public abstract <T> T build();
    }

    protected String name;
    protected String a;
    protected String b;
    protected String c;
    protected Z z = null;

    protected TestParam() {

    }

    protected TestParam(String name, String a, String b, String c)
    {
        this.name = name;
        this.a = a;
        this.b = b;
        this.c = c;
    }

    protected TestParam(String name, String a, String b, String c, Z z)
    {
        this.name = name;
        this.a = a;
        this.b = b;
        this.c = c;
        this.z = z;
    }

    public String getA() 
    {
        return a;
    }

    public String getB()
    {
        return b;
    }

    public String getC()
    {
        return c;
    }

    protected abstract String getContent();

    @Override
    public String toString()
    {
        return name+"[A: " + a + ", B: " + b + ", C: " + c + (z != null ? ", Z: " + z.toString() : "") + getContent() +"]";
    }
}

A child class with a none-static builder looks like this:

@SuppressWarnings({"hiding", "unchecked"})
public class TestParamA<D,E,Z> extends TestParam<Z>
{
    public class Builder<T extends TestParamA<D,E,Z>, 
                         B extends TestParamA<D,E,Z>.Builder<? extends TestParamA<D,E,Z>, ? extends B, D, E>, 
                         D,E> 
                 extends TestParam<Z>.CommonBuilder<Builder<TestParamA<D,E,Z>,B, D,E>>
    {
        protected D d;
        protected E e;

        public Builder(String a, String b, String c)
        {
            super(a, b, c);
        }

        public B withD(D d)
        {
            this.d = d;
            return (B)this;
        }

        public B withE(E e)
        {
            this.e = e;
            return (B)this;
        }

        @Override
        public <T> T build()
        {
            TestParamA<D,E,Z> t = new TestParamA<>("TestParamA", a, b, c, z, d, e);
            return (T)t;
        }        
    }

    protected D d;
    protected E e;

    public TestParamA() {
        super();
    }

    protected TestParamA(String name, String a, String b, String c, Z z, D d, E e)
    {
        super(name, a, b, c, z);
        this.d = d;
        this.e = e;
    }

    public D getD()
    {
        return d;
    }

    public E getE()
    {
        return e;
    }

    @Override
    protected String getContent()
    {
        return ", D: " + d + ", E: " + e;
    }
}

To test the functionality of this outer/inner classes you can implement something like this:

public class Main
{
    public static void main(String ... args)
    {
        TestParamA<D,E,String> a = new TestParamA<>().new Builder<>("a","b","c").withD(new D()).withE(new E()).build();
        TestParamB<F,G,String> b = new TestParamB<>().new Builder<>("a","b","c").withF(new F()).withG(new G()).withOptionalZ("z").build();
        TestParam<String> c = new TestParamA<>().new Builder<>("a","b","c").withD(new D()).withE(new E()).withOptionalZ("z").build();
        TestParam<?> d = new TestParamB<>().new Builder<>("a","b","c").withF(new F()).withG(new G()).build();

        test(a);
        test(b);
        test(c);
        test(d);

        TestParam<?>.CommonBuilder<? extends TestParam<?>.CommonBuilder<?>> builder = 
            new TestParamA<>().new Builder<>("a", "b", "c").withD(new D()).withE(new E());
        test(builder);
        // or a bit shorter
        TestParam<?>.CommonBuilder<?> builder2 = 
            new TestParamB<>().new Builder<>("a", "b", "c").withF(new F()).withG(new G());
        test(builder2);
    }

    public static void test(TestParamA<?,?,?> testParam)
    {
        System.out.println("Test for ParamA: " + testParam.toString());
    }

    public static void test(TestParamB<?,?,?> testParam)
    {
        System.out.println("Test for ParamB: " + testParam.toString());
    }

    public static void test(TestParam<?> testParam)
    {
        System.out.println("Test for Param: " + testParam.toString());
    }

    public static void test(TestParam<?>.CommonBuilder<?> builder)
    {
        System.out.println("Test for CommonBuilder: " + builder.build().toString());
    }
}

TestParamB is identical to TestParamA - it only contains varialbe and builder-methods for F and G instead of D and E . Furthermore, D , E , F and G are only classes with a simple toString() implementation which returns just the simple classname.

This will print the following output:

Test for ParamA: TestParamA[A: a, B: b, C: c, D: D, E: E]
Test for ParamB: TestParamB[A: a, B: b, C: c, Z: z, F: F, G: G]
Test for Param: TestParamA[A: a, B: b, C: c, Z: z, D: D, E: E]
Test for Param: TestParamB[A: a, B: b, C: c, F: F, G: G]
Test for CommonBuilder: TestParamA[A: a, B: b, C: c, D: D, E: E]
Test for CommonBuilder: TestParamB[A: a, B: b, C: c, F: F, G: G]

However the iInstance variable shows an error when adding the generics part

Type arguments given on a raw type

First of all, that's not the problem you should be getting, because InnerClass is not defined in the scope. Being an inner class, it is scoped inside the outer class's scope. So when it is outside the outer class, you need to explicitly qualify it with an outer class, or it will give you a InnerClass symbol not found error. So you are not showing your real code (maybe you have another InnerClass somewhere) or not show your real error.

If I remove the generics part from variables, then the below test cases will fail with type errors

When you use a raw type to access members, that turns off all generics on those members. So .parse() is erased to public OuterClass parse(Class clazz) (this is true even though CT is not used by the method), and that's why oInstance.parse(ExtendedOuter.class) returns type OuterClass which is not compatible with ExtendedOuter .

How can I add <?> to iInstance without making InnerClass static?

Like OuterClass<?>.InnerClass<?> , or OuterClass<Something>.InnerClass<SomethingElse>

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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