简体   繁体   中英

How to instantiate recursive bound generics with inner class in Java?

I would like to have the generic outer class with inner-class as parameter. I was expecting, that I will derive from base class and either use it's inner, or derive inner too. At each level I was expecting to limit inner class usage starting from current level of deriving.

Unfortunately, I meeting various errors and warnings when using the pattern so that I can't imagine, how to use it.

package tests.java;

public class Try_GenericInnerRecursion {

    // base class, consisting of outer and inner parts
    public static class Outer1<E extends Outer1<E>.Inner1> {
        public class Inner1 {
        }

        public void addElement(E e) {
            System.out.println("Added " + e.toString());
        }
    }

    // extending outer, but not inner
    public static class Outer2<E extends Outer1<E>.Inner1> extends Outer1<E>{

    }

    // extending both outer and inner
    public static class Outer3<E extends Outer3<E>.Inner3> extends Outer1<E>{
        public class Inner3 extends Inner1 {
        }
    }

    // extending both outer and inner and stopping extension
    public static class Outer4 extends Outer1<Outer4.Inner4> {
        public class Inner4 extends Outer1<Inner4>.Inner1 {
        }
    }



    // instantiating
    public static void main(String[] args) {

        Outer1<Outer1.Inner1> a1; // WARNING: Outer1.Inner1 is a raw type

        a1 = new Outer1<Outer1.Inner1>(); // WARNING: Outer1.Inner1 is a raw type

        Outer1<?> a2; // OK 

        a2 = new Outer1<?>(); // ERROR: Cannot instantiate the type Outer1<?>

        Outer1<Outer1<?>.Inner1> a3; // ERROR: Bound mismatch: The type Outer1<?>.Inner1 is not a valid substitute for the bounded parameter <E extends Outer1<E>.Inner1> of the type Outer1<E>

    Outer1<? extends Outer1<?>.Inner1> a4; // OK

    a4 = new Outer1<Outer1.Inner1>(); // ERROR: Type mismatch

            Outer2<Outer1.Inner1> b1; // WARNING: Outer1.Inner1 is a raw type

            b1 = new Outer2<Outer1.Inner1>(); // WARNING: Outer1.Inner1 is a raw type

        // and so on
    }

}

How can I use this pattern correctly?

I believe you can simply do

    DerivedCorpus1<?>

during wildcard capture, it becomes

    DerivedCorpus1<x> where x extends Corpus<x>.Element

which is correctly bounded.

In your exmaple

    DerivedCorpus1<? extends Corpus<?>.Element>

during wildcard capture, it becomes

    DerivedCorpus1<x> where x extends Corpus<x>.Element
                        and x extends Corpus<?>.Element

apparently, the extends Corpus<?>.Element clause is redundant.

Outer1<Outer1.Inner1> a1; // WARNING: Outer1.Inner1 is a raw type

Actually, I get "type argument Outer1.Inner1 is not within bounds of type-variable E`".

Outer1.Inner1 is a raw type, because Outer1 is a raw type. To use a not-raw type, you would need to write Outer1<something>.Inner1 . However, that something would also have to extend Outer1<something>.Inner1 in turn. In order to have recursion like this you need a named recursive type. Unfortunately, since Inner1 is a non-static inner class, it has an implicit reference to an instance of Outer1 , and so any class that extends it would also need to have an enclosing instance of Outer1 . Outer4 and Inner4 basically do this.

Outer4 a1 = new Outer4(); // compiles fine

a2 = new Outer1<?>(); // ERROR: Cannot instantiate the type Outer1<?>

You can never do new something<?>() .

Outer1<Outer1<?>.Inner1> a3; // ERROR: Bound mismatch: The type Outer1<?>.Inner1 is not a valid substitute for the bounded parameter <E extends Outer1<E>.Inner1> of the type Outer1<E>

This is true. Outer1<?>.Inner1 is not a subtype of Outer1<E>.Inner1 -- it's the other way around -- Outer1<E>.Inner1 is a subtype of Outer1<?>.Inner1 . This is just like how ArrayList<?> is not a subtype of ArrayList<String> ; it's the other way around.

Outer1<? extends Outer1<?>.Inner1> a4; // OK

This is okay because you have a wildcard at the top level, and your wildcard's bound intersects the type parameter E 's bound. In fact, anything that satisfies E 's original bound must satisfy this bound, so this bound is useless, and this is the same as Outer1<?> a2; above.

a4 = new Outer1<Outer1.Inner1>(); // ERROR: Type mismatch

Doesn't work, for, among other reasons, the same reason that a1 doesn't work ( Outer1.Inner1 does not satisfy E 's bound). It additionally doesn't satisfy your bound ( Outer1<?>.Inner1 ) I believe.

Outer2<Outer1.Inner1> b1; // WARNING: Outer1.Inner1 is a raw type

This actually gives the same error as a1 , for the same reason

Generic specification

Before I go on to the instantiation problems I revise your generic definition:

Outer1<E extends Outer1<E>.Inner1>

Outer1 should basically hold elements of type Inner1 or any child classes of it. Therefore the type of the Outer1 object is not of relevance and can be replaced with the following code:

Outer1<E extends Outer1<?>.Innter1>

Also the extending classes have been slightly modified using the above mentioned.

Instantiation

For the instantiation of objects you stated the following:

 Outer1<Outer1.Inner1> a1; // WARNING: Outer1.Inner1 is a raw type a1 = new Outer1<Outer1.Inner1>(); // WARNING: Outer1.Inner1 is a raw type 

As newacct has already mentioned, Outer1 is declared with a generic type though you didn't specify any while instantiating the object. The code therefore should be changed to the following one:

Outer1<Outer1<?>.Innter1> a1;           // or simply Outer1<?> a1;
a1 = new Outer1<Outer1<?>.Innter1>();   // or simply a1 = new Outer1<>();

Similar for assigning classes which extended a base class to a type of that base class.

You specify with the generic type definition what type of elements the instance will hold.

A declaration like

Outer1<Outer1<?>.Innter1> test;

will accept any instances extending Outer1 that also match the generic type. This means you can only assign

test = new Outer1<Outer1<?>.Inner1>();
test = new Outer2<Outer2<?>.Inner1>();

to it, but not Outer3 or even Outer4 !

Even if you modify the code further to add fe

public static class Outer5<E extends Outer1<?>.Inner1> extends Outer1<E>{
    public class Inner5 extends Inner1 {
    }
}

you are just able to add

test = new Outer5<Outer5<?>.Inner1>();

but not

test = new Outer5<Outer5<?>.Inner5>(); // FAILS!

to something that defined Inner1 as type argument.

However, there is actually a quite simple solution to this issue. If you define the object like this:

Outer1<? extends Outer1<?>.Inner1> test2;

you are actually able to do the following things:

test2 = new Outer1<Outer1<?>.Inner1>();
test2 = new Outer2<Outer2<?>.Inner1>();
test2 = new Outer3<Outer3<?>.Inner3>();
test2 = new Outer4();
test2 = new Outer5<Outer1<?>.Inner1>();
test2 = new Outer5<Outer5<?>.Inner1>();
test2 = new Outer5<Outer5<?>.Inner5>();

as you now explicitly told the compiler that either the Inner1 type itself or any of its extensions is allowed.

Code

I've updated your code and added a couple of new assignments to verify that the changes made actually do not produce any compile errors or warnings except for the redundant type arguments in new expression due to explicitly specifying the type arguments instead of using the diamond operator <> :

public class GenericInnerRecursion
{
    // base class, consisting of outer and inner parts
    public static class Outer1<E extends Outer1<?>.Inner1> {
        public class Inner1 {
        }

        public void addElement(E e) {
            System.out.println("Added " + e.toString());
        }
    }

    // extending outer, but not inner
    public static class Outer2<E extends Outer1<?>.Inner1> extends Outer1<E>{

    }

    // extending both outer and inner
    public static class Outer3<E extends Outer3<?>.Inner3> extends Outer1<E>{
        public class Inner3 extends Inner1 {
        }
    }

    // extending both outer and inner and stopping extension
    public static class Outer4 extends Outer1<Outer4.Inner4> {
        public class Inner4 extends Outer1<Inner4>.Inner1 {
        }
    }

    public static class Outer5<E extends Outer1<?>.Inner1> extends Outer1<E>{
        public class Inner5 extends Inner1 {
        }
    }


    // instantiating
    public static void main(String[] args) {

        Outer1<Outer1<?>.Inner1> a1;
        a1 = new Outer1<Outer1<?>.Inner1>();

        Outer1<?> a2;
        a2 = new Outer1<>();

        Outer1<Outer1<?>.Inner1> a3; 

        Outer1<? extends Outer1<?>.Inner1> a4;
        a4 = new Outer1<Outer1<?>.Inner1>();

        Outer2<Outer1<?>.Inner1> b1;
        b1 = new Outer2<Outer1<?>.Inner1>();

        // and so on

        // assigning extension-classes to the parent-class
        Outer1<Outer1<?>.Inner1> c1;
        c1 = new Outer2<Outer2<?>.Inner1>();
        // assigning inner-extension-classes to parent-class
        Outer1<Outer3<?>.Inner3> c2;
        c2 = new Outer3<Outer3<?>.Inner3>();
        // assigning extension class without specified generics to parent class
        Outer1<Outer4.Inner4> c3;
        c3 = new Outer4();

        Outer1<Outer1<?>.Inner1> test;
        test = new Outer1<>();
        test = new Outer2<>();
        test = new Outer5<Outer5<?>.Inner1>();

        Outer1<? extends Outer1<?>.Inner1> test2;
        test2 = new Outer1<Outer1<?>.Inner1>();
        test2 = new Outer2<Outer2<?>.Inner1>();
        test2 = new Outer3<Outer3<?>.Inner3>();
        // new Outer3<Outer3<?>.Inner1>(); not possible as generic type extends Outer3<?>.Inner3 and not Outer1<?>.Inner1!
        test2 = new Outer4();
        test2 = new Outer5<Outer1<?>.Inner1>();
        test2 = new Outer5<Outer5<?>.Inner1>();
        test2 = new Outer5<Outer5<?>.Inner5>();
    }
}

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