循环泛型:接口IFoo <T扩展IFoo <T >>

Mar*_*tus 9 java generics

这是Java中一个众所周知的习语.例如,参见SO讨论.所以基本上定义一个接口,实现它的类需要有一个方法来与你的类的对象进行比较:

public interface IComparable<T extends IComparable<T>> {
    public int compare(T item);
}
Run Code Online (Sandbox Code Playgroud)

(注意:这显然是解决特定用例的一种不必要的复杂方法 - 请参阅此帖 -但我正在询问如何解释递归语法,更不用说特定的应用程序).

简而言之,这是一个递归定义,在递归中没有明显的结束,我没有看到编译器/类型系统如何解决这个问题.

此外,尝试输入单词("IComparable是一个类,其实例可以与实现IComparable接口的类的对象进行比较")产生一个不合逻辑的循环定义,这在哲学/逻辑/数学中是不可能的,但显然是在编译器设计中可行(!).

此外,似乎如果原始的习语语法是可接受的,那么也可以允许一个人说:

public interface IComparable<T extends IComparable<T extends IComparable<T>>> {
    public int compare(T item);
}
Run Code Online (Sandbox Code Playgroud)

...但编译器显然只允许一个级别extend.

任何人都可以帮助我理解这种递归的通用定义吗?

UPDATE

根据接受的答案(BartoszKP),我认为我现在理解的是:

不应将"递归"定义读作(表示"定义取决于"关系的箭头):

[ IComparable<T> ] -------> [ IComparable<T> ]
Run Code Online (Sandbox Code Playgroud)

......这是不合逻辑的(循环的),而是:

[ IComparable<T> ] ----------> [ list of methods (parameterized by T) ]
          \                                      ^
           \                                     |
            \-------> [ type bound on T ]-------/
Run Code Online (Sandbox Code Playgroud)

...这不是不合逻辑的,因此在编译器中可行.因此,在文字中,定义IComparable是根据它定义的方法列表和它在类型上定义的界限给出的,T后者又取决于方法列表.所以,没有递归.

new*_*cct 5

这是Java中一个众所周知的习语.

不,它没用,也不应该使用.除了Enum(由于enum类型是编译器生成的特殊情况),这种模式在官方Java库或任何官方Java文档中的任何地方都不会出现.

类型变量上的泛型边界的目的是建立一个关系,以便泛型类或泛型方法中的代码可以使用该保证来执行某些操作而不需要强制转换,这可能是不安全的.泛型边界应该尽可能不受限制,同时仍然阻止代码中的强制转换.

因此,绑定的唯一合法目的interface IComparable<T extends IComparable<T>>是1)它实际上是一个类,而不是接口(接口没有代码,因此没有要避免的强制转换),以及2)类中的代码需要执行以下操作:使用一种方法T脱离自身,然后调用一个需要的方法IComparable<T>.然后,T延伸的保证IComparable<T>将允许在没有强制转换的情况下完成.

我已经看到了这种模式的许多用途,并且在99%的情况下,他们不需要进行上述操作.相反,99%的时间,人们想要做的是以某种方式表达T"等于" IComparable<T>,上述模式不能保证(并且在Java中无法表达).它只保证T延伸IComparable<T>,但不是IComparable<T>延伸T.

大多数时候,当人们使用这种模式时,他们会将它用于他们所做的课程(T)this.需要进行演员表明这可能是不安全的; 它的类型安全性不受边界保证this(不保证(类型IComparable<T>)延伸T).它实际上是不安全的; 一个众所周知的例子是class Foo extends IComparable<Foo>,class Bar extends IComparable<Foo>因此this(类型Bar)不会扩展T(Foo).

因此,如果您看到该代码,几乎可以肯定地写了一些关于它的作用的误解,它应该几乎总是被改为:

public interface IComparable<T> {
    public int compare(T item);
}
Run Code Online (Sandbox Code Playgroud)

作为练习,我挑战你找到一个工作的片段interface IComparable<T extends IComparable<T>>,哪些interface IComparable<T>不起作用.


Bar*_*zKP 2

首先,关于您对复发的疑虑:

它是某种递归(因为 X 是由与 X 相关的事物定义的),但即使是这样,也只是一个级别。想象一下编译器有一个函数define,它通过读取和编译源文件定义了一些它还不知道的东西,还有一个实用程序函数get_list_of_methods。所以在这种情况下ResultItem我们有:

1)define(interface ResultItem<T extends ResultItem<T>>)

为此,ResultItem<T>需要对内部进行定义。但我们不需要知道关于 的一切ResultItem<T>,我们只想知道某事物扩展意味着什么ResultItem<T>,因此首先:

2)get_list_of_methods(ResultItem<T>)

这会解析源文件,获取所有方法的列表ResultItem<T>- 我们现在不必编译它们。

3)创建通用约束保护,它将T具有以下所有方法ResultItem<T>

4) 继续步骤 1) - 解析源文件,获取ResultItem<T>. 从步骤 2中知道正在实现哪些方法,T我们可以编译这些方法。

实际上这可能更复杂,但这有助于看出不需要重复来解释这一点。所以它并不比class X拥有一个接受 的方法更频繁X。它只需要不止一张通行证。在较旧的语言中,这样的lookahead定义是不可能的。即使在 C++ 中,现在也需要前向声明:

class X;

class Y
{
private:
  X* x;
};

class X
{
};
Run Code Online (Sandbox Code Playgroud)

接下来,关于这个的可用性。从评论和相关主题中收集信息,我想说这种机制用于表达类实现给定接口的意图,并使接口更加有用和方便。本主题更详细地解释了这一点:如何使接口实例方法仅接受同一类的参数?

在第二种情况下,实现接口的类不会获得任何便利:

public interface IComparable<T extends IComparable<T>> {
  public int compare(T item);
}
Run Code Online (Sandbox Code Playgroud)

然而,它可以被解释为表达这样一个事实:一个实体comparable只能与同类的可比较项目进行比较。正如其他答案和评论中所述,这不是最好的例子,因为class X implements IComparable<Y>只要 Y 也具有可比性,您仍然可以写。更多详细信息,请参阅:How can I make an interface instance method 仅接受同一类的参数,真的吗?

有关此惯用语用法的更多详细信息:http://www.angelikalanger.com/GenericsFAQ/FAQSections/TypeParameters.html#FAQ106