Java子类化或实现具有相同边界的泛型类型

时间:2017-05-31 18:27:20

标签: java generics

简而言之,我的问题是,是否,

interface Intf<T extends Bound> {}
class Clss<T extends Bound> {}

除了重复绑定类型之外,没有办法继承/实现它们:

class Impl<T extends Bound> implements Intf<T> {}
class Subc<T extends Bound> extends Clss<T> {}

我没有抱怨需要重复extends Bound,我甚至理解编译逻辑,但我想知道这是否真的是这样。因为在两个地方使用<T>在语义上看起来都足够了。

2 个答案:

答案 0 :(得分:1)

是的,就是这样。这是每个人的充分解释。

这是你的例子的另一个版本(想象一下Integer可以扩展)

interface Intf<T extends Number> {}
class Clss<T extends Number> {}
class Impl<T extends Integer> implements Intf<T> {}
class Subc<T extends Integer> extends Clss<T> {}
class ImplSubc<T extends Integer> extends Clss<T> implements Intf<T> {}

<T extends Integer>是初始化标记,其他<T>标记基本上是“传递”引用。这两个标签在视觉上是相同的,但它们是不同的东西。请注意以下内容是非法的

class Subc<T extends Integer> extends Clss<T extends Integer> {}

因为传递标记需要SOMETHING ELSE来初始化它。所以你可以知道哪个是哪个,因为initialize标签最终会传递给它,而传递标签是根据初始化标签设置的内容传递(或返回)的东西。所以如果你有以下

class Subc<T> extends Clss<T> {}

编译单元说“允许将其设置为任何内容,然后将其传递给Clss”。由于您可以将初始化标记传递给多个传递标记,每个标记都有不同的边界,初始化标记NEEDS将被明确设置为确保传入的<T>确实与您传递的所有内容兼容它一直到。虽然这在技术上可以推断,但是它昂贵,复杂,并且要求可怕的生产破坏错误,因此编译器只会迫使您知道自己在做什么。

更新:
下面是一个示例,说明如果让编译器决定您的代码是什么,错误如何进入生产。假设编译器确实推断了限制,If

class Clss<T extends Number> {}
class Subc<T> extends Clss<T> {}

成为

class Clss<T extends BigDecimal> {}
class Subc<T> extends Clss<T> {}

如果没有在{2}上设置extends Integer并让它被推断,则此更改将进行编译(如果将其编译为与将其用作API的内容分开编译),并且可能仍然有效,直到一个案例没有。因为您说您明确希望使用Integer或Number,所以现在可以确定更改Clss的类型在编译时而不是运行时是安全的或坏的更改。 Java是一种安全第一类型语言,这意味着代码稳定性/可靠性检测是其功能的核心价值。所以当有疑问时,Java会抱怨。

答案 1 :(得分:1)

请注意,尽管Foo<T [constraints]> implements Bar<T>模式很常见,但它只有一种可能性。以下是其他一些(非详尽的):

Foo<T extends DerivedBound>        implements Bar<T>
Foo<T>                             implements Bar<DerivedBound>
Foo<T>                             implements Bar<ParameterisedBound<T>>   
Foo<T, U extends Bound>            implements Bar<U>
Foo<T extends BarBound & BazBound> implements Bar<T>, Baz<T>

因此,必须两次指定绑定没有冗余 - 您要向编译器提供两条独立的信息。

理论上,语言设计者可以在Java中添加一个特殊情况,以节省一些字符。但假设他们甚至考虑过这个选项,我想他们认为额外的复杂性并不是一个很好的权衡。