如何在Java中用内部类实例化递归绑定泛型?

时间:2015-05-18 19:36:41

标签: java generics

我想将内部类作为参数的泛型外部类。我期待,我将从基类派生出来,或者使用它的内部,或者从内部派生出来。在每个级别,我都希望从当前的派生级别开始限制内部类别的使用。

不幸的是,我在使用模式时会遇到各种错误和警告,以至于我无法想象,如何使用它。

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
    }

}

如何正确使用此模式?

3 个答案:

答案 0 :(得分:3)

我相信你可以做到

    DerivedCorpus1<?>

在通配符捕获期间,它变为

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

这是正确的限制。

在你的例子中

    DerivedCorpus1<? extends Corpus<?>.Element>

在通配符捕获期间,它变为

    DerivedCorpus1<x> where x extends Corpus<x>.Element
                        and x extends Corpus<?>.Element
显然,extends Corpus<?>.Element子句是多余的。

答案 1 :(得分:3)

  

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

实际上,我得到“类型参数Outer1.Inner1不在类型变量E`的范围内。”

Outer1.Inner1是原始类型,因为Outer1是原始类型。要使用非原始类型,您需要编写Outer1<something>.Inner1。但是,something也必须依次延长Outer1<something>.Inner1。为了像这样进行递归,你需要一个命名的递归类型。不幸的是,由于Inner1是一个非静态内部类,它有一个对Outer1实例的隐式引用,因此扩展它的任何类都需要有一个{{1的实例}}。 Outer1Outer4基本上就是这样做的。

Inner4
  

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

你永远不能a2 = new Outer1<?>(); // ERROR: Cannot instantiate the type Outer1<?>

  

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>不是Outer1<?>.Inner1的子类型 - 反之亦然 - Outer1<E>.Inner1Outer1<E>.Inner1的子类型。这就像Outer1<?>.Inner1不是ArrayList<?>的子类型一样;反过来了。

  

ArrayList<String>

这没关系,因为你在顶层有一个通配符,你的通配符的边界与类型参数Outer1<? extends Outer1<?>.Inner1> a4; // OK的绑定相交。实际上,任何满足E原始边界的东西都必须满足这个边界,所以这个边界是无用的,这与上面的E相同。

  

Outer1<?> a2;

由于a4 = new Outer1<Outer1.Inner1>(); // ERROR: Type mismatch不起作用的相同原因(a1不满足Outer1.Inner1的限制),因此不起作用。它还不满足你的约束(E)。

  

Outer1<?>.Inner1

由于同样的原因,这实际上会产生与Outer2<Outer1.Inner1> b1; // WARNING: Outer1.Inner1 is a raw type相同的错误

答案 2 :(得分:1)

通用规范

在我继续讨论实例化问题之前,我修改了你的通用定义:

Outer1<E extends Outer1<E>.Inner1>

Outer1基本上应该包含Inner1类型的元素或者它的任何子类。因此,Outer1对象的类型不相关,可以用以下代码替换:

Outer1<E extends Outer1<?>.Innter1>

使用上面提到的扩展类也稍作修改。

<强>实例化

对于对象的实例化,您说明了以下内容:

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

正如newacct已经提到的那样,虽然在实例化对象时没有指定任何类型,但Outer1被声明为泛型类型。因此,代码应更改为以下代码:

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

类似于分配将基类扩展为该基类类型的类。

使用泛型类型定义指定实例将包含的元素类型。

这样的宣言
Outer1<Outer1<?>.Innter1> test;

将接受任何扩展Outer1且与通用类型匹配的实例。这意味着您只能分配

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

,但不是Outer3甚至Outer4

即使您进一步修改代码以添加f.e.

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

你只能添加

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

但不是

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

Inner1定义为类型参数。

然而,这个问题实际上是一个非常简单的解决方案。如果你定义这样的对象:

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

你实际上可以做以下事情:

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>();

正如您现在明确告诉编译器允许Inner1类型本身或其任何扩展名。

<强>代码

我已更新您的代码并添加了一些新的分配,以验证所做的更改实际上不会产生任何编译错误或警告,除了redundant type arguments in new expression由于显式指定类型参数而不是使用钻石运算符<>

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